@weapp-tailwindcss/cli 5.4.1 → 5.5.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 CHANGED
@@ -53,6 +53,8 @@ const exitCode = await runCli(['-i', 'src/input.css', '-o', 'dist/output.css'])
53
53
 
54
54
  Importing the package does not execute the CLI. `runCli` follows the same stdout, stderr, and `process.exitCode` behavior as the binary.
55
55
 
56
+ Use `weapp-tw doctor` when integrating or upgrading a project. It only reports duplicate Tailwind generation when a configuration actually references `@tailwindcss/vite` or `@tailwindcss/postcss`; a plain PostCSS config is not treated as an error.
57
+
56
58
  ## Documentation
57
59
 
58
60
  See the [CLI guide](https://tw.weapp.dev/docs/tools/weapp-tw-cli) for all options, stdin/stdout, source maps, watch mode, and `canonicalize`.
package/README.zh-CN.md CHANGED
@@ -56,3 +56,5 @@ const exitCode = await runCli(['-i', 'src/input.css', '-o', 'dist/output.css'])
56
56
  ## 文档
57
57
 
58
58
  完整参数、stdin/stdout、source map、watch 与 `canonicalize` 用法见 [CLI 使用指南](https://tw.weapp.dev/zh-cn/docs/tools/weapp-tw-cli)。
59
+
60
+ 接入或升级排障时,可以运行 `weapp-tw doctor`。它会检查实际配置中是否注册了官方 Tailwind 生成器;仅存在普通 PostCSS 配置不会触发重复生成警告。
package/dist/bin.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const require_src = require("./src-CrXWQNny.cjs");
2
+ const require_src = require("./src-C4Y7BTX1.cjs");
3
3
  let node_process = require("node:process");
4
4
  node_process = require_src.__toESM(node_process, 1);
5
5
  //#region src/bin.ts
package/dist/bin.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { t as runCli } from "./src-BfHGXT3j.js";
2
+ import { t as runCli } from "./src-Dpl0bugL.js";
3
3
  import process from "node:process";
4
4
  //#region src/bin.ts
5
5
  process.title = "node (@weapp-tailwindcss/cli)";
package/dist/index.cjs CHANGED
@@ -1,3 +1,3 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_src = require("./src-CrXWQNny.cjs");
2
+ const require_src = require("./src-C4Y7BTX1.cjs");
3
3
  exports.runCli = require_src.runCli;
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- import { t as runCli } from "./src-BfHGXT3j.js";
1
+ import { t as runCli } from "./src-Dpl0bugL.js";
2
2
  export { runCli };
@@ -475,7 +475,7 @@ async function runTailwindCli(rawArgv) {
475
475
  }
476
476
  //#endregion
477
477
  //#region package.json
478
- var version = "5.4.1";
478
+ var version = "5.5.0";
479
479
  //#endregion
480
480
  //#region src/constants.ts
481
481
  const WEAPP_TW_REQUIRED_NODE_VERSION_RANGE = "^22.18.0 || >=24.11.0";
@@ -522,6 +522,90 @@ const FRAMEWORK_DEPS = [
522
522
  ["remax", "Remax"]
523
523
  ];
524
524
  //#endregion
525
+ //#region src/doctor/semantics.ts
526
+ function readConfigText(cwd, file) {
527
+ if (!file) return;
528
+ try {
529
+ return (0, node_fs.readFileSync)(node_path.default.join(cwd, file), "utf8");
530
+ } catch {
531
+ return;
532
+ }
533
+ }
534
+ function stripConfigComments(source) {
535
+ return source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/(?<!:)\/\/.*$/gmu, "");
536
+ }
537
+ function findConfiguredTailwindOwners(cwd, files) {
538
+ const owners = /* @__PURE__ */ new Set();
539
+ const evidence = [];
540
+ for (const file of files) {
541
+ const source = readConfigText(cwd, file);
542
+ if (!source) continue;
543
+ const config = stripConfigComments(source);
544
+ if (/['"]@tailwindcss\/vite['"]/u.test(config)) {
545
+ owners.add("vite");
546
+ evidence.push(file);
547
+ }
548
+ if (/['"]@tailwindcss\/postcss['"]/u.test(config)) {
549
+ owners.add("postcss");
550
+ evidence.push(file);
551
+ }
552
+ }
553
+ return {
554
+ owners,
555
+ evidence: [...new Set(evidence)]
556
+ };
557
+ }
558
+ function extractConfiguredCssEntries(source) {
559
+ if (!source) return [];
560
+ const match = stripConfigComments(source).match(/cssEntries\s*:\s*\[([\s\S]*?)\]/u);
561
+ if (!match?.[1]) return [];
562
+ return [...match[1].matchAll(/["']([^"']+\.css)["']/gu)].map((item) => item[1]).filter(Boolean);
563
+ }
564
+ function extractConfiguredOption(source, option) {
565
+ if (!source) return;
566
+ return stripConfigComments(source).match(new RegExp(`\\b${option}\\s*:\\s*["']([^"']+)["']`, "u"))?.[1];
567
+ }
568
+ function extractGeneratorTarget(source) {
569
+ if (!source) return;
570
+ const config = stripConfigComments(source);
571
+ const generator = config.match(/generator\s*:\s*\{([\s\S]*?)\}/u)?.[1];
572
+ return extractConfiguredOption(generator, "target") ?? extractConfiguredOption(config, "target");
573
+ }
574
+ function extractCssSources(source) {
575
+ const directives = [];
576
+ for (const match of source.matchAll(/@source\s+(?:(not)\s+)?(?:(inline)\s*\(\s*)?["']([^"']*)["']/gu)) {
577
+ const value = match[3]?.trim();
578
+ if (value) directives.push({
579
+ value,
580
+ excluded: Boolean(match[1]),
581
+ inline: Boolean(match[2])
582
+ });
583
+ }
584
+ return directives;
585
+ }
586
+ function resolveSourceProbePath(cssFile, source) {
587
+ const wildcard = source.search(/[!*?[{]/u);
588
+ const prefix = wildcard >= 0 ? source.slice(0, wildcard) : source;
589
+ const directory = wildcard >= 0 ? node_path.default.dirname(prefix) : prefix;
590
+ return node_path.default.resolve(node_path.default.dirname(cssFile), directory || ".");
591
+ }
592
+ const SUPPORTED_APP_TYPES = /* @__PURE__ */ new Set([
593
+ "uni-app",
594
+ "uni-app-vite",
595
+ "uni-app-x",
596
+ "taro",
597
+ "remax",
598
+ "native",
599
+ "kbone",
600
+ "mpx",
601
+ "weapp-vite"
602
+ ]);
603
+ const SUPPORTED_GENERATOR_TARGETS = /* @__PURE__ */ new Set([
604
+ "web",
605
+ "weapp",
606
+ "app"
607
+ ]);
608
+ //#endregion
525
609
  //#region src/doctor.ts
526
610
  function tryReadJson(file) {
527
611
  try {
@@ -561,7 +645,11 @@ function detectFrameworks(deps) {
561
645
  return FRAMEWORK_DEPS.filter(([dependency]) => dependency in deps).map(([, label]) => label);
562
646
  }
563
647
  function addCheck(checks, check) {
564
- checks.push(check);
648
+ checks.push({
649
+ ...check,
650
+ code: check.code ?? check.id,
651
+ evidence: check.evidence ?? []
652
+ });
565
653
  }
566
654
  function summarizeChecks(checks) {
567
655
  return checks.reduce((summary, check) => {
@@ -593,13 +681,37 @@ function createDoctorReport(options = {}) {
593
681
  const checks = [];
594
682
  const packageManager = detectPackageManager(cwd, pkg);
595
683
  const frameworks = detectFrameworks(deps);
596
- const tailwindcssVersion = readDependencyVersion(cwd, "tailwindcss");
597
- const weappTailwindcssVersion = readDependencyVersion(cwd, "weapp-tailwindcss");
684
+ const tailwindcssVersion = hasDependency(deps, "tailwindcss") ? readDependencyVersion(cwd, "tailwindcss") : void 0;
685
+ const weappTailwindcssVersion = hasDependency(deps, "weapp-tailwindcss") ? readDependencyVersion(cwd, "weapp-tailwindcss") : void 0;
598
686
  const tailwindMajor = getMajorVersion(tailwindcssVersion) ?? getDependencyMajor(deps, "tailwindcss");
599
687
  const tailwindConfig = findFirstExisting(cwd, CONFIG_FILES.tailwind);
600
688
  const postcssConfig = findFirstExisting(cwd, CONFIG_FILES.postcss);
601
689
  const viteConfig = findFirstExisting(cwd, CONFIG_FILES.vite);
602
690
  const webpackConfig = findFirstExisting(cwd, CONFIG_FILES.webpack);
691
+ const tailwindOwners = findConfiguredTailwindOwners(cwd, [
692
+ postcssConfig,
693
+ viteConfig,
694
+ webpackConfig
695
+ ]);
696
+ const cssEntries = [
697
+ postcssConfig,
698
+ viteConfig,
699
+ webpackConfig
700
+ ].map((file) => ({
701
+ file,
702
+ source: readConfigText(cwd, file)
703
+ })).flatMap(({ file, source }) => extractConfiguredCssEntries(source).map((entry) => ({
704
+ file,
705
+ entry
706
+ })));
707
+ const buildConfigs = [
708
+ postcssConfig,
709
+ viteConfig,
710
+ webpackConfig
711
+ ].map((file) => ({
712
+ file,
713
+ source: readConfigText(cwd, file)
714
+ })).filter((item) => Boolean(item.file && item.source));
603
715
  addCheck(checks, pkg ? {
604
716
  id: "package-json",
605
717
  title: "package.json",
@@ -683,13 +795,112 @@ function createDoctorReport(options = {}) {
683
795
  message: "未检测到 postcss.config.*。",
684
796
  suggestion: "如果通过 PostCSS 接入,请补充 postcss.config.*;如果通过 Vite/Taro 插件接入,可忽略此项。"
685
797
  });
686
- if (tailwindMajor === 4 && postcssConfig && !hasDependency(deps, "@tailwindcss/postcss")) addCheck(checks, {
687
- id: "tailwindcss-v4-postcss",
688
- title: "Tailwind v4 PostCSS",
798
+ if (tailwindMajor === 4 && tailwindOwners.owners.size > 0) addCheck(checks, {
799
+ id: "tailwindcss-generator-owner",
800
+ title: "Tailwind 生成器归属",
689
801
  status: "warn",
690
- message: "Tailwind CSS v4 项目存在 PostCSS 配置,但未检测到 @tailwindcss/postcss。",
691
- suggestion: "如果 PostCSS 配置中仍直接使用 tailwindcss,请迁移到 @tailwindcss/postcss。"
802
+ message: `检测到官方 Tailwind ${[...tailwindOwners.owners].join(" 与 ")} 生成器配置。生成模式下不要与 WeappTailwindcss 同时运行两套 Tailwind 生成器。`,
803
+ suggestion: "移除 @tailwindcss/vite 或 @tailwindcss/postcss 的生成配置,仅保留 WeappTailwindcss;PostCSS 中可以继续保留其他业务插件。",
804
+ code: "duplicate-tailwind-generator",
805
+ evidence: tailwindOwners.evidence
692
806
  });
807
+ if (tailwindMajor === 4 && cssEntries.length > 0) {
808
+ const missingEntries = cssEntries.filter(({ entry }) => !(0, node_fs.existsSync)(node_path.default.resolve(cwd, entry)));
809
+ const entriesWithoutTailwindImport = cssEntries.filter(({ entry }) => {
810
+ if (!(0, node_fs.existsSync)(node_path.default.resolve(cwd, entry))) return false;
811
+ try {
812
+ const source = (0, node_fs.readFileSync)(node_path.default.resolve(cwd, entry), "utf8");
813
+ return !/@import\s+["']tailwindcss["']/u.test(source) && !/@import\s+["']tailwindcss\//u.test(source);
814
+ } catch {
815
+ return false;
816
+ }
817
+ });
818
+ if (missingEntries.length > 0) addCheck(checks, {
819
+ id: "tailwind-css-entry",
820
+ title: "Tailwind CSS 入口",
821
+ status: "error",
822
+ message: `配置了不存在的 cssEntries:${missingEntries.map((item) => item.entry).join(", ")}。`,
823
+ suggestion: "请将 cssEntries 改为项目根目录解析出的绝对路径,或确认入口文件已创建。",
824
+ code: "missing-css-entry",
825
+ evidence: missingEntries.map((item) => item.file).filter((file) => Boolean(file))
826
+ });
827
+ if (entriesWithoutTailwindImport.length > 0) addCheck(checks, {
828
+ id: "tailwind-css-entry-import",
829
+ title: "Tailwind CSS 入口内容",
830
+ status: "warn",
831
+ message: `cssEntries 指向的文件未发现 @import "tailwindcss":${entriesWithoutTailwindImport.map((item) => item.entry).join(", ")}。`,
832
+ suggestion: "请在纯 CSS 入口中加入 @import \"tailwindcss\",并确保该文件被应用构建图实际引入。",
833
+ code: "missing-tailwind-import",
834
+ evidence: entriesWithoutTailwindImport.map((item) => node_path.default.resolve(cwd, item.entry))
835
+ });
836
+ cssEntries.flatMap(({ entry }) => {
837
+ const cssFile = node_path.default.resolve(cwd, entry);
838
+ if (!(0, node_fs.existsSync)(cssFile)) return [];
839
+ let source;
840
+ try {
841
+ source = (0, node_fs.readFileSync)(cssFile, "utf8");
842
+ } catch {
843
+ return [];
844
+ }
845
+ const directives = extractCssSources(source);
846
+ const diagnostics = [];
847
+ if (!directives.some((directive) => !directive.excluded)) diagnostics.push({
848
+ id: "tailwind-css-source",
849
+ title: "Tailwind CSS 来源范围",
850
+ status: "warn",
851
+ message: `${entry} 未发现 @source,Tailwind 可能无法扫描项目源码。`,
852
+ suggestion: "请在 CSS 入口中加入覆盖实际源码目录的 @source;仅配置 cssEntries 不会替代源码扫描声明。",
853
+ code: "missing-tailwind-source",
854
+ evidence: [cssFile]
855
+ });
856
+ for (const directive of directives) {
857
+ if (directive.inline || directive.excluded) continue;
858
+ const probePath = resolveSourceProbePath(cssFile, directive.value);
859
+ if (!(0, node_fs.existsSync)(probePath)) diagnostics.push({
860
+ id: "tailwind-css-source-path",
861
+ title: "Tailwind @source 路径",
862
+ status: "warn",
863
+ message: `@source ${directive.value} 的路径不存在或无法从入口解析。`,
864
+ suggestion: "请按 CSS 入口所在目录修正 @source 相对路径,或确认源码目录已经创建。",
865
+ code: "invalid-tailwind-source",
866
+ evidence: [cssFile, probePath]
867
+ });
868
+ }
869
+ return diagnostics;
870
+ }).forEach((check) => addCheck(checks, check));
871
+ }
872
+ for (const { file, source } of buildConfigs) {
873
+ const appType = extractConfiguredOption(source, "appType");
874
+ if (appType && !SUPPORTED_APP_TYPES.has(appType)) addCheck(checks, {
875
+ id: "app-type",
876
+ title: "appType 配置",
877
+ status: "error",
878
+ message: `${file} 使用了不受支持的 appType:${appType}。`,
879
+ suggestion: "请改为 uni-app、uni-app-x、taro、mpx、weapp-vite 或其他公开支持的 appType。",
880
+ code: "invalid-app-type",
881
+ evidence: [file]
882
+ });
883
+ const target = extractGeneratorTarget(source);
884
+ if (target && !SUPPORTED_GENERATOR_TARGETS.has(target)) addCheck(checks, {
885
+ id: "generator-target",
886
+ title: "生成目标",
887
+ status: "error",
888
+ message: `${file} 使用了不受支持的 generator.target:${target}。`,
889
+ suggestion: "请使用 web、weapp 或 app;普通 Vite Web 项目推荐使用 web。",
890
+ code: "invalid-generator-target",
891
+ evidence: [file]
892
+ });
893
+ const platform = extractConfiguredOption(source, "platform");
894
+ if (platform && target && (platform === "web" && target !== "web" || platform !== "web" && target === "web")) addCheck(checks, {
895
+ id: "generator-platform",
896
+ title: "生成目标与平台",
897
+ status: "warn",
898
+ message: `${file} 的 platform=${platform} 与 generator.target=${target} 可能不一致。`,
899
+ suggestion: "请让 platform 与 generator.target 指向同一运行端,或删除不必要的显式覆盖以使用自动推断。",
900
+ code: "generator-target-platform-conflict",
901
+ evidence: [file]
902
+ });
903
+ }
693
904
  addCheck(checks, frameworks.length > 0 ? {
694
905
  id: "framework",
695
906
  title: "框架识别",
@@ -446,7 +446,7 @@ async function runTailwindCli(rawArgv) {
446
446
  }
447
447
  //#endregion
448
448
  //#region package.json
449
- var version = "5.4.1";
449
+ var version = "5.5.0";
450
450
  //#endregion
451
451
  //#region src/constants.ts
452
452
  const WEAPP_TW_REQUIRED_NODE_VERSION_RANGE = "^22.18.0 || >=24.11.0";
@@ -493,6 +493,90 @@ const FRAMEWORK_DEPS = [
493
493
  ["remax", "Remax"]
494
494
  ];
495
495
  //#endregion
496
+ //#region src/doctor/semantics.ts
497
+ function readConfigText(cwd, file) {
498
+ if (!file) return;
499
+ try {
500
+ return readFileSync(path.join(cwd, file), "utf8");
501
+ } catch {
502
+ return;
503
+ }
504
+ }
505
+ function stripConfigComments(source) {
506
+ return source.replace(/\/\*[\s\S]*?\*\//gu, "").replace(/(?<!:)\/\/.*$/gmu, "");
507
+ }
508
+ function findConfiguredTailwindOwners(cwd, files) {
509
+ const owners = /* @__PURE__ */ new Set();
510
+ const evidence = [];
511
+ for (const file of files) {
512
+ const source = readConfigText(cwd, file);
513
+ if (!source) continue;
514
+ const config = stripConfigComments(source);
515
+ if (/['"]@tailwindcss\/vite['"]/u.test(config)) {
516
+ owners.add("vite");
517
+ evidence.push(file);
518
+ }
519
+ if (/['"]@tailwindcss\/postcss['"]/u.test(config)) {
520
+ owners.add("postcss");
521
+ evidence.push(file);
522
+ }
523
+ }
524
+ return {
525
+ owners,
526
+ evidence: [...new Set(evidence)]
527
+ };
528
+ }
529
+ function extractConfiguredCssEntries(source) {
530
+ if (!source) return [];
531
+ const match = stripConfigComments(source).match(/cssEntries\s*:\s*\[([\s\S]*?)\]/u);
532
+ if (!match?.[1]) return [];
533
+ return [...match[1].matchAll(/["']([^"']+\.css)["']/gu)].map((item) => item[1]).filter(Boolean);
534
+ }
535
+ function extractConfiguredOption(source, option) {
536
+ if (!source) return;
537
+ return stripConfigComments(source).match(new RegExp(`\\b${option}\\s*:\\s*["']([^"']+)["']`, "u"))?.[1];
538
+ }
539
+ function extractGeneratorTarget(source) {
540
+ if (!source) return;
541
+ const config = stripConfigComments(source);
542
+ const generator = config.match(/generator\s*:\s*\{([\s\S]*?)\}/u)?.[1];
543
+ return extractConfiguredOption(generator, "target") ?? extractConfiguredOption(config, "target");
544
+ }
545
+ function extractCssSources(source) {
546
+ const directives = [];
547
+ for (const match of source.matchAll(/@source\s+(?:(not)\s+)?(?:(inline)\s*\(\s*)?["']([^"']*)["']/gu)) {
548
+ const value = match[3]?.trim();
549
+ if (value) directives.push({
550
+ value,
551
+ excluded: Boolean(match[1]),
552
+ inline: Boolean(match[2])
553
+ });
554
+ }
555
+ return directives;
556
+ }
557
+ function resolveSourceProbePath(cssFile, source) {
558
+ const wildcard = source.search(/[!*?[{]/u);
559
+ const prefix = wildcard >= 0 ? source.slice(0, wildcard) : source;
560
+ const directory = wildcard >= 0 ? path.dirname(prefix) : prefix;
561
+ return path.resolve(path.dirname(cssFile), directory || ".");
562
+ }
563
+ const SUPPORTED_APP_TYPES = /* @__PURE__ */ new Set([
564
+ "uni-app",
565
+ "uni-app-vite",
566
+ "uni-app-x",
567
+ "taro",
568
+ "remax",
569
+ "native",
570
+ "kbone",
571
+ "mpx",
572
+ "weapp-vite"
573
+ ]);
574
+ const SUPPORTED_GENERATOR_TARGETS = /* @__PURE__ */ new Set([
575
+ "web",
576
+ "weapp",
577
+ "app"
578
+ ]);
579
+ //#endregion
496
580
  //#region src/doctor.ts
497
581
  function tryReadJson(file) {
498
582
  try {
@@ -532,7 +616,11 @@ function detectFrameworks(deps) {
532
616
  return FRAMEWORK_DEPS.filter(([dependency]) => dependency in deps).map(([, label]) => label);
533
617
  }
534
618
  function addCheck(checks, check) {
535
- checks.push(check);
619
+ checks.push({
620
+ ...check,
621
+ code: check.code ?? check.id,
622
+ evidence: check.evidence ?? []
623
+ });
536
624
  }
537
625
  function summarizeChecks(checks) {
538
626
  return checks.reduce((summary, check) => {
@@ -564,13 +652,37 @@ function createDoctorReport(options = {}) {
564
652
  const checks = [];
565
653
  const packageManager = detectPackageManager(cwd, pkg);
566
654
  const frameworks = detectFrameworks(deps);
567
- const tailwindcssVersion = readDependencyVersion(cwd, "tailwindcss");
568
- const weappTailwindcssVersion = readDependencyVersion(cwd, "weapp-tailwindcss");
655
+ const tailwindcssVersion = hasDependency(deps, "tailwindcss") ? readDependencyVersion(cwd, "tailwindcss") : void 0;
656
+ const weappTailwindcssVersion = hasDependency(deps, "weapp-tailwindcss") ? readDependencyVersion(cwd, "weapp-tailwindcss") : void 0;
569
657
  const tailwindMajor = getMajorVersion(tailwindcssVersion) ?? getDependencyMajor(deps, "tailwindcss");
570
658
  const tailwindConfig = findFirstExisting(cwd, CONFIG_FILES.tailwind);
571
659
  const postcssConfig = findFirstExisting(cwd, CONFIG_FILES.postcss);
572
660
  const viteConfig = findFirstExisting(cwd, CONFIG_FILES.vite);
573
661
  const webpackConfig = findFirstExisting(cwd, CONFIG_FILES.webpack);
662
+ const tailwindOwners = findConfiguredTailwindOwners(cwd, [
663
+ postcssConfig,
664
+ viteConfig,
665
+ webpackConfig
666
+ ]);
667
+ const cssEntries = [
668
+ postcssConfig,
669
+ viteConfig,
670
+ webpackConfig
671
+ ].map((file) => ({
672
+ file,
673
+ source: readConfigText(cwd, file)
674
+ })).flatMap(({ file, source }) => extractConfiguredCssEntries(source).map((entry) => ({
675
+ file,
676
+ entry
677
+ })));
678
+ const buildConfigs = [
679
+ postcssConfig,
680
+ viteConfig,
681
+ webpackConfig
682
+ ].map((file) => ({
683
+ file,
684
+ source: readConfigText(cwd, file)
685
+ })).filter((item) => Boolean(item.file && item.source));
574
686
  addCheck(checks, pkg ? {
575
687
  id: "package-json",
576
688
  title: "package.json",
@@ -654,13 +766,112 @@ function createDoctorReport(options = {}) {
654
766
  message: "未检测到 postcss.config.*。",
655
767
  suggestion: "如果通过 PostCSS 接入,请补充 postcss.config.*;如果通过 Vite/Taro 插件接入,可忽略此项。"
656
768
  });
657
- if (tailwindMajor === 4 && postcssConfig && !hasDependency(deps, "@tailwindcss/postcss")) addCheck(checks, {
658
- id: "tailwindcss-v4-postcss",
659
- title: "Tailwind v4 PostCSS",
769
+ if (tailwindMajor === 4 && tailwindOwners.owners.size > 0) addCheck(checks, {
770
+ id: "tailwindcss-generator-owner",
771
+ title: "Tailwind 生成器归属",
660
772
  status: "warn",
661
- message: "Tailwind CSS v4 项目存在 PostCSS 配置,但未检测到 @tailwindcss/postcss。",
662
- suggestion: "如果 PostCSS 配置中仍直接使用 tailwindcss,请迁移到 @tailwindcss/postcss。"
773
+ message: `检测到官方 Tailwind ${[...tailwindOwners.owners].join(" 与 ")} 生成器配置。生成模式下不要与 WeappTailwindcss 同时运行两套 Tailwind 生成器。`,
774
+ suggestion: "移除 @tailwindcss/vite 或 @tailwindcss/postcss 的生成配置,仅保留 WeappTailwindcss;PostCSS 中可以继续保留其他业务插件。",
775
+ code: "duplicate-tailwind-generator",
776
+ evidence: tailwindOwners.evidence
663
777
  });
778
+ if (tailwindMajor === 4 && cssEntries.length > 0) {
779
+ const missingEntries = cssEntries.filter(({ entry }) => !existsSync(path.resolve(cwd, entry)));
780
+ const entriesWithoutTailwindImport = cssEntries.filter(({ entry }) => {
781
+ if (!existsSync(path.resolve(cwd, entry))) return false;
782
+ try {
783
+ const source = readFileSync(path.resolve(cwd, entry), "utf8");
784
+ return !/@import\s+["']tailwindcss["']/u.test(source) && !/@import\s+["']tailwindcss\//u.test(source);
785
+ } catch {
786
+ return false;
787
+ }
788
+ });
789
+ if (missingEntries.length > 0) addCheck(checks, {
790
+ id: "tailwind-css-entry",
791
+ title: "Tailwind CSS 入口",
792
+ status: "error",
793
+ message: `配置了不存在的 cssEntries:${missingEntries.map((item) => item.entry).join(", ")}。`,
794
+ suggestion: "请将 cssEntries 改为项目根目录解析出的绝对路径,或确认入口文件已创建。",
795
+ code: "missing-css-entry",
796
+ evidence: missingEntries.map((item) => item.file).filter((file) => Boolean(file))
797
+ });
798
+ if (entriesWithoutTailwindImport.length > 0) addCheck(checks, {
799
+ id: "tailwind-css-entry-import",
800
+ title: "Tailwind CSS 入口内容",
801
+ status: "warn",
802
+ message: `cssEntries 指向的文件未发现 @import "tailwindcss":${entriesWithoutTailwindImport.map((item) => item.entry).join(", ")}。`,
803
+ suggestion: "请在纯 CSS 入口中加入 @import \"tailwindcss\",并确保该文件被应用构建图实际引入。",
804
+ code: "missing-tailwind-import",
805
+ evidence: entriesWithoutTailwindImport.map((item) => path.resolve(cwd, item.entry))
806
+ });
807
+ cssEntries.flatMap(({ entry }) => {
808
+ const cssFile = path.resolve(cwd, entry);
809
+ if (!existsSync(cssFile)) return [];
810
+ let source;
811
+ try {
812
+ source = readFileSync(cssFile, "utf8");
813
+ } catch {
814
+ return [];
815
+ }
816
+ const directives = extractCssSources(source);
817
+ const diagnostics = [];
818
+ if (!directives.some((directive) => !directive.excluded)) diagnostics.push({
819
+ id: "tailwind-css-source",
820
+ title: "Tailwind CSS 来源范围",
821
+ status: "warn",
822
+ message: `${entry} 未发现 @source,Tailwind 可能无法扫描项目源码。`,
823
+ suggestion: "请在 CSS 入口中加入覆盖实际源码目录的 @source;仅配置 cssEntries 不会替代源码扫描声明。",
824
+ code: "missing-tailwind-source",
825
+ evidence: [cssFile]
826
+ });
827
+ for (const directive of directives) {
828
+ if (directive.inline || directive.excluded) continue;
829
+ const probePath = resolveSourceProbePath(cssFile, directive.value);
830
+ if (!existsSync(probePath)) diagnostics.push({
831
+ id: "tailwind-css-source-path",
832
+ title: "Tailwind @source 路径",
833
+ status: "warn",
834
+ message: `@source ${directive.value} 的路径不存在或无法从入口解析。`,
835
+ suggestion: "请按 CSS 入口所在目录修正 @source 相对路径,或确认源码目录已经创建。",
836
+ code: "invalid-tailwind-source",
837
+ evidence: [cssFile, probePath]
838
+ });
839
+ }
840
+ return diagnostics;
841
+ }).forEach((check) => addCheck(checks, check));
842
+ }
843
+ for (const { file, source } of buildConfigs) {
844
+ const appType = extractConfiguredOption(source, "appType");
845
+ if (appType && !SUPPORTED_APP_TYPES.has(appType)) addCheck(checks, {
846
+ id: "app-type",
847
+ title: "appType 配置",
848
+ status: "error",
849
+ message: `${file} 使用了不受支持的 appType:${appType}。`,
850
+ suggestion: "请改为 uni-app、uni-app-x、taro、mpx、weapp-vite 或其他公开支持的 appType。",
851
+ code: "invalid-app-type",
852
+ evidence: [file]
853
+ });
854
+ const target = extractGeneratorTarget(source);
855
+ if (target && !SUPPORTED_GENERATOR_TARGETS.has(target)) addCheck(checks, {
856
+ id: "generator-target",
857
+ title: "生成目标",
858
+ status: "error",
859
+ message: `${file} 使用了不受支持的 generator.target:${target}。`,
860
+ suggestion: "请使用 web、weapp 或 app;普通 Vite Web 项目推荐使用 web。",
861
+ code: "invalid-generator-target",
862
+ evidence: [file]
863
+ });
864
+ const platform = extractConfiguredOption(source, "platform");
865
+ if (platform && target && (platform === "web" && target !== "web" || platform !== "web" && target === "web")) addCheck(checks, {
866
+ id: "generator-platform",
867
+ title: "生成目标与平台",
868
+ status: "warn",
869
+ message: `${file} 的 platform=${platform} 与 generator.target=${target} 可能不一致。`,
870
+ suggestion: "请让 platform 与 generator.target 指向同一运行端,或删除不必要的显式覆盖以使用自动推断。",
871
+ code: "generator-target-platform-conflict",
872
+ evidence: [file]
873
+ });
874
+ }
664
875
  addCheck(checks, frameworks.length > 0 ? {
665
876
  id: "framework",
666
877
  title: "框架识别",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@weapp-tailwindcss/cli",
3
3
  "type": "module",
4
- "version": "5.4.1",
4
+ "version": "5.5.0",
5
5
  "description": "Cross-platform Tailwind CSS CLI for Web and mini-program builds. 面向 Web 与小程序构建的跨端 Tailwind CSS CLI。",
6
6
  "author": "ice breaker <1324318532@qq.com>",
7
7
  "license": "MIT",
@@ -55,7 +55,7 @@
55
55
  "fast-glob": "^3.3.3",
56
56
  "lightningcss": "^1.33.0",
57
57
  "semver": "~7.8.5",
58
- "weapp-tailwindcss": "5.4.1"
58
+ "weapp-tailwindcss": "5.5.0"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "tsdown",