@zealakit/css-doctor-next 1.0.0 → 1.1.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/dist/index.js CHANGED
@@ -360,7 +360,10 @@ var CssDoctorWebpackPlugin = class {
360
360
  pageUsage[page.name] = classes;
361
361
  pageUsageCounts[page.name] = classes.length;
362
362
  }
363
- const pageCoverageApplied = this.pageCoverage.enabled && pageNames.length > 0;
363
+ const hasPageClasses = pagesResult.pages.some(
364
+ (page) => page.classes.size > 0
365
+ );
366
+ const pageCoverageApplied = this.pageCoverage.enabled && hasPageClasses;
364
367
  const diagnostics = {
365
368
  plugin: "css-doctor-next",
366
369
  generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -449,7 +452,7 @@ var CssDoctorWebpackPlugin = class {
449
452
  cssContent: originalCss,
450
453
  pageUsage,
451
454
  pageCoverage: {
452
- enabled: this.pageCoverage.enabled,
455
+ enabled: pageCoverageApplied,
453
456
  minPageRatio: this.pageCoverage.minPageRatio,
454
457
  forceCommon: this.pageCoverage.forceCommon
455
458
  },
@@ -709,6 +712,7 @@ async function collectPages(compilation, directPageUsage) {
709
712
  const pageEntries = [];
710
713
  const sharedEntries = [];
711
714
  const fallbackEntries = [];
715
+ const loaderFallbackEntries = [];
712
716
  for (const entry of entries) {
713
717
  const name = entryName(entry);
714
718
  if (!name) {
@@ -725,12 +729,19 @@ async function collectPages(compilation, directPageUsage) {
725
729
  name: routeNameForEntry(name) ?? name,
726
730
  sourceName: name
727
731
  });
732
+ } else {
733
+ loaderFallbackEntries.push({
734
+ entry,
735
+ name: routeNameForEntry(name) ?? name,
736
+ sourceName: name
737
+ });
728
738
  }
729
739
  }
730
740
  const selectedPages = pageEntries.length > 0 ? pageEntries : fallbackEntries;
731
741
  const direct = directPageUsage ? Object.keys(directPageUsage).length > 0 : false;
732
742
  const pages = /* @__PURE__ */ new Map();
733
743
  const pagesByCss = /* @__PURE__ */ new Map();
744
+ const projectRoot = compilation.compiler?.context ?? process.cwd();
734
745
  if (direct && directPageUsage) {
735
746
  for (const [rawName, classes] of Object.entries(directPageUsage)) {
736
747
  const name = rawName.trim();
@@ -744,10 +755,27 @@ async function collectPages(compilation, directPageUsage) {
744
755
  jsFiles: /* @__PURE__ */ new Set()
745
756
  });
746
757
  }
758
+ attachEntryAssetsToPages(pages, selectedPages);
759
+ const orphanPages = [...pages.values()].filter(
760
+ (page) => page.jsFiles.size === 0
761
+ );
762
+ if (orphanPages.length > 0) {
763
+ const loaderEntries = selectedPages.length > 0 ? selectedPages : loaderFallbackEntries;
764
+ const fallbackJs = /* @__PURE__ */ new Set();
765
+ for (const { entry } of loaderEntries) {
766
+ for (const jsFile of collectEntryJsFiles(entry)) {
767
+ fallbackJs.add(jsFile);
768
+ }
769
+ }
770
+ for (const page of orphanPages) {
771
+ for (const jsFile of fallbackJs) {
772
+ page.jsFiles.add(jsFile);
773
+ }
774
+ }
775
+ }
747
776
  } else {
748
777
  const sourceCache = /* @__PURE__ */ new Map();
749
778
  const sharedClasses = /* @__PURE__ */ new Set();
750
- const projectRoot = process.cwd();
751
779
  for (const entry of sharedEntries) {
752
780
  for (const className of await collectEntryClasses(
753
781
  compilation,
@@ -800,19 +828,10 @@ async function collectPages(compilation, directPageUsage) {
800
828
  const targetPages = [...pages.keys()];
801
829
  for (const { entry, name } of selectedPages) {
802
830
  const cssFiles = collectEntryCssFiles(entry);
803
- if (direct) {
804
- const directPage = pages.get(name);
805
- if (directPage) {
806
- for (const cssFile of cssFiles) {
807
- directPage.cssFiles.add(cssFile);
808
- }
809
- }
810
- } else {
811
- const page = pages.get(name);
812
- if (page) {
813
- for (const cssFile of cssFiles) {
814
- page.cssFiles.add(cssFile);
815
- }
831
+ const page = findPageForEntry(pages, name);
832
+ if (page) {
833
+ for (const cssFile of cssFiles) {
834
+ page.cssFiles.add(cssFile);
816
835
  }
817
836
  }
818
837
  }
@@ -837,18 +856,46 @@ async function collectPages(compilation, directPageUsage) {
837
856
  pagesByCss
838
857
  };
839
858
  }
859
+ function attachEntryAssetsToPages(pages, selectedPages) {
860
+ for (const { entry, name } of selectedPages) {
861
+ const page = findPageForEntry(pages, name);
862
+ if (!page) {
863
+ continue;
864
+ }
865
+ for (const jsFile of collectEntryJsFiles(entry)) {
866
+ page.jsFiles.add(jsFile);
867
+ }
868
+ for (const cssFile of collectEntryCssFiles(entry)) {
869
+ page.cssFiles.add(cssFile);
870
+ }
871
+ }
872
+ }
873
+ function findPageForEntry(pages, entryNameValue) {
874
+ const direct = pages.get(entryNameValue);
875
+ if (direct) {
876
+ return direct;
877
+ }
878
+ const normalized = normalizeSlashes(entryNameValue).replace(/^\/+/, "");
879
+ const bare = normalized.replace(/^\//, "");
880
+ return pages.get(normalized) ?? pages.get(`/${bare}`) ?? pages.get(bare) ?? pages.get(bare.replace(/\//g, "-"));
881
+ }
840
882
  async function collectPageSourceClasses(entryName2, projectRoot, sourceCache) {
841
883
  const classes = /* @__PURE__ */ new Set();
842
884
  const visited = /* @__PURE__ */ new Set();
843
885
  const normalized = normalizeSlashes(entryName2);
886
+ const roots = projectSourceRoots(projectRoot);
844
887
  const candidates = [];
845
888
  if (normalized.startsWith("app/")) {
846
889
  const path = normalized.slice(4);
847
890
  if (path === "page" || path.endsWith("/page")) {
848
891
  const pagePath = path === "page" ? "app/page" : `app/${path}`;
849
- candidates.push(resolve2(projectRoot, pagePath));
892
+ for (const root of roots) {
893
+ candidates.push(resolve2(root, pagePath));
894
+ }
850
895
  if (path === "_not-found/page") {
851
- candidates.push(resolve2(projectRoot, "app/not-found"));
896
+ for (const root of roots) {
897
+ candidates.push(resolve2(root, "app/not-found"));
898
+ }
852
899
  }
853
900
  const directories = path.split("/").slice(0, -1).filter(Boolean);
854
901
  const layoutPaths = ["app"];
@@ -856,16 +903,24 @@ async function collectPageSourceClasses(entryName2, projectRoot, sourceCache) {
856
903
  layoutPaths.push(`app/${directories.slice(0, index + 1).join("/")}`);
857
904
  }
858
905
  for (const layoutPath of layoutPaths) {
859
- candidates.push(resolve2(projectRoot, `${layoutPath}/layout`));
906
+ for (const root of roots) {
907
+ candidates.push(resolve2(root, `${layoutPath}/layout`));
908
+ }
860
909
  }
861
910
  } else {
862
- candidates.push(resolve2(projectRoot, normalized));
911
+ for (const root of roots) {
912
+ candidates.push(resolve2(root, normalized));
913
+ }
863
914
  }
864
915
  } else if (normalized.startsWith("pages/")) {
865
- candidates.push(resolve2(projectRoot, normalized));
866
- candidates.push(resolve2(projectRoot, "pages/_app"));
916
+ for (const root of roots) {
917
+ candidates.push(resolve2(root, normalized));
918
+ candidates.push(resolve2(root, "pages/_app"));
919
+ }
867
920
  } else {
868
- candidates.push(resolve2(projectRoot, normalized));
921
+ for (const root of roots) {
922
+ candidates.push(resolve2(root, normalized));
923
+ }
869
924
  }
870
925
  for (const candidate of candidates) {
871
926
  const file = findSourceFile(candidate);
@@ -878,13 +933,23 @@ async function collectPageSourceClasses(entryName2, projectRoot, sourceCache) {
878
933
  async function collectNameSourceClasses(entryName2, projectRoot, sourceCache) {
879
934
  const classes = /* @__PURE__ */ new Set();
880
935
  const visited = /* @__PURE__ */ new Set();
881
- const candidate = resolve2(projectRoot, normalizeSlashes(entryName2));
882
- const file = findSourceFile(candidate);
883
- if (file) {
884
- await scanSourceFile(file, classes, projectRoot, sourceCache, visited);
936
+ const relative = normalizeSlashes(entryName2);
937
+ for (const root of projectSourceRoots(projectRoot)) {
938
+ const file = findSourceFile(resolve2(root, relative));
939
+ if (file) {
940
+ await scanSourceFile(file, classes, projectRoot, sourceCache, visited);
941
+ }
885
942
  }
886
943
  return classes;
887
944
  }
945
+ function projectSourceRoots(projectRoot) {
946
+ const roots = [projectRoot];
947
+ const srcRoot = resolve2(projectRoot, "src");
948
+ if (srcRoot !== projectRoot && existsSync2(srcRoot)) {
949
+ roots.push(srcRoot);
950
+ }
951
+ return roots;
952
+ }
888
953
  async function scanSourceFile(filePath, classes, projectRoot, sourceCache, visited) {
889
954
  const absolutePath = resolve2(filePath);
890
955
  if (visited.has(absolutePath)) {
@@ -968,18 +1033,28 @@ function extractImportSpecifiers(source) {
968
1033
  return [...specifiers];
969
1034
  }
970
1035
  function resolveImportSpecifier(specifier, fromFile, projectRoot) {
971
- let basePath;
972
1036
  if (specifier.startsWith("@/")) {
973
- basePath = resolve2(projectRoot, specifier.slice(2));
974
- } else if (specifier.startsWith("./") || specifier.startsWith("../")) {
975
- basePath = resolve2(dirname2(fromFile), specifier);
976
- } else {
1037
+ const relative = specifier.slice(2);
1038
+ for (const root of projectSourceRoots(projectRoot)) {
1039
+ const basePath = resolve2(root, relative);
1040
+ if (isStyleFile(basePath)) {
1041
+ return null;
1042
+ }
1043
+ const found = findSourceFile(basePath);
1044
+ if (found) {
1045
+ return found;
1046
+ }
1047
+ }
977
1048
  return null;
978
1049
  }
979
- if (isStyleFile(basePath)) {
980
- return null;
1050
+ if (specifier.startsWith("./") || specifier.startsWith("../")) {
1051
+ const basePath = resolve2(dirname2(fromFile), specifier);
1052
+ if (isStyleFile(basePath)) {
1053
+ return null;
1054
+ }
1055
+ return findSourceFile(basePath);
981
1056
  }
982
- return findSourceFile(basePath);
1057
+ return null;
983
1058
  }
984
1059
  async function collectEntryClasses(compilation, entry, sourceCache) {
985
1060
  const classes = /* @__PURE__ */ new Set();
@@ -1148,13 +1223,22 @@ function scanClasses(source, classes) {
1148
1223
  continue;
1149
1224
  }
1150
1225
  for (const token of value.split(/\s+/)) {
1151
- if (/^-?[_a-zA-Z][\w-]*$/.test(token)) {
1226
+ if (isClassToken(token)) {
1152
1227
  classes.add(token.toLowerCase());
1153
1228
  }
1154
1229
  }
1155
1230
  }
1156
1231
  }
1157
1232
  }
1233
+ function isClassToken(token) {
1234
+ if (!token || token.length > 256) {
1235
+ return false;
1236
+ }
1237
+ if (/[`${}]/.test(token)) {
1238
+ return false;
1239
+ }
1240
+ return /[A-Za-z_]/.test(token);
1241
+ }
1158
1242
  function addCssOwner(pagesByCss, cssFile, page) {
1159
1243
  const owners = pagesByCss.get(cssFile) ?? /* @__PURE__ */ new Set();
1160
1244
  owners.add(page);