@geoql/doctor-core 1.4.0 → 1.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
@@ -21,7 +21,7 @@ It also owns the pieces the CLIs surface: the config loader (`doctor.config.ts`
21
21
  npm i @geoql/doctor-core
22
22
  ```
23
23
 
24
- Published on [npm](https://www.npmjs.com/package/@geoql/doctor-core) and [JSR](https://jsr.io/@geoql/doctor-core) at `v0.1.0` with provenance. ESM-only. `knip` and `oxlint` are peer dependencies (the dead-code and script passes shell out to them).
24
+ Published on [npm](https://www.npmjs.com/package/@geoql/doctor-core) and [JSR](https://jsr.io/@geoql/doctor-core) with provenance. ESM-only. `knip` and `oxlint` are peer dependencies (the dead-code and script passes shell out to them).
25
25
 
26
26
  ## API
27
27
 
package/dist/index.js CHANGED
@@ -337,7 +337,7 @@ async function buildKnipConfig(projectInfo, doctorConfig) {
337
337
  "!**/knip.config.mjs"
338
338
  ],
339
339
  ignoreFiles: [...doctorConfig.exclude, "knip.config.mjs"],
340
- ignoreDependencies: [...new Set([
340
+ ignoreDependencies: [.../* @__PURE__ */ new Set([
341
341
  "vite-plus",
342
342
  "@geoql/vue-doctor",
343
343
  "@geoql/nuxt-doctor",
@@ -808,9 +808,10 @@ const RULE_ID$17 = "nuxt-doctor/nitro/compatibilityDate-set";
808
808
  function checkCompatibilityDateSet(projectInfo, nuxtConfig) {
809
809
  if (projectInfo.packageJsonPath === null) return [];
810
810
  if (nuxtConfig?.compatibilityDate !== void 0) return [];
811
+ const file = projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath;
811
812
  return [{
812
813
  ruleId: RULE_ID$17,
813
- file: projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath,
814
+ file,
814
815
  line: 1,
815
816
  column: 1,
816
817
  severity: "error",
@@ -824,9 +825,10 @@ const RULE_ID$16 = "nuxt-doctor/seo/lang-on-html";
824
825
  function checkLangOnHtml(projectInfo, nuxtConfig) {
825
826
  if (projectInfo.packageJsonPath === null) return [];
826
827
  if (nuxtConfig?.htmlLang !== void 0) return [];
828
+ const file = projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath;
827
829
  return [{
828
830
  ruleId: RULE_ID$16,
829
- file: projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath,
831
+ file,
830
832
  line: 1,
831
833
  column: 1,
832
834
  severity: "warn",
@@ -837,7 +839,7 @@ function checkLangOnHtml(projectInfo, nuxtConfig) {
837
839
  //#endregion
838
840
  //#region src/nuxt/post-checks/nitro-cloudflare-preset.ts
839
841
  const RULE_ID$15 = "nuxt-doctor/cloudflare/nitro-cloudflare-preset";
840
- const CLOUDFLARE_PRESETS = new Set([
842
+ const CLOUDFLARE_PRESETS = /* @__PURE__ */ new Set([
841
843
  "cloudflare-pages",
842
844
  "cloudflare-module",
843
845
  "cloudflare"
@@ -847,9 +849,10 @@ function checkNitroCloudflarePreset(projectInfo) {
847
849
  if (!projectInfo.hasWranglerConfig) return [];
848
850
  if (projectInfo.nitroPreset === null) return [];
849
851
  if (CLOUDFLARE_PRESETS.has(projectInfo.nitroPreset)) return [];
852
+ const file = projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath;
850
853
  return [{
851
854
  ruleId: RULE_ID$15,
852
- file: projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath,
855
+ file,
853
856
  line: 1,
854
857
  column: 1,
855
858
  severity: "warn",
@@ -860,7 +863,7 @@ function checkNitroCloudflarePreset(projectInfo) {
860
863
  //#endregion
861
864
  //#region src/nuxt/post-checks/no-modules-incompatible-with-nuxt-4.ts
862
865
  const RULE_ID$14 = "nuxt-doctor/modules-deps/no-modules-incompatible-with-nuxt-4";
863
- const INCOMPATIBLE_MODULES = new Set([
866
+ const INCOMPATIBLE_MODULES = /* @__PURE__ */ new Set([
864
867
  "@nuxtjs/composition-api",
865
868
  "@nuxt/bridge",
866
869
  "nuxt-property-decorator"
@@ -884,7 +887,7 @@ async function checkNoModulesIncompatibleWithNuxt4(projectInfo) {
884
887
  //#endregion
885
888
  //#region src/nuxt/post-checks/no-node-only-modules.ts
886
889
  const RULE_ID$13 = "nuxt-doctor/cloudflare/no-node-only-modules";
887
- const NODE_ONLY_MODULES = new Set(["fs-extra", "sharp"]);
890
+ const NODE_ONLY_MODULES = /* @__PURE__ */ new Set(["fs-extra", "sharp"]);
888
891
  async function checkNoNodeOnlyModules(projectInfo) {
889
892
  if (projectInfo.packageJsonPath === null) return [];
890
893
  if (!projectInfo.hasWranglerConfig) return [];
@@ -1030,7 +1033,7 @@ async function checkOgImageViaSatori(projectInfo) {
1030
1033
  if (!projectInfo.hasWranglerConfig) return [];
1031
1034
  const deps = await readDeps(projectInfo.packageJsonPath);
1032
1035
  if (deps === null) return [];
1033
- const installed = new Set([...Object.keys(deps.dependencies), ...Object.keys(deps.devDependencies)]);
1036
+ const installed = /* @__PURE__ */ new Set([...Object.keys(deps.dependencies), ...Object.keys(deps.devDependencies)]);
1034
1037
  if (OG_IMAGE_MODULES.some((name) => installed.has(name))) return [];
1035
1038
  return [{
1036
1039
  ruleId: RULE_ID$10,
@@ -1049,9 +1052,10 @@ function checkPresetDefinedForDeployTarget(projectInfo) {
1049
1052
  if (projectInfo.packageJsonPath === null) return [];
1050
1053
  if (!projectInfo.hasWranglerConfig) return [];
1051
1054
  if (projectInfo.nitroPreset !== null) return [];
1055
+ const file = projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath;
1052
1056
  return [{
1053
1057
  ruleId: RULE_ID$9,
1054
- file: projectInfo.nuxtConfigPath ?? projectInfo.packageJsonPath,
1058
+ file,
1055
1059
  line: 1,
1056
1060
  column: 1,
1057
1061
  severity: "warn",
@@ -1080,7 +1084,7 @@ async function checkRecommendedModulesInstalled(projectInfo) {
1080
1084
  if (projectInfo.packageJsonPath === null) return [];
1081
1085
  const deps = await readDeps(projectInfo.packageJsonPath);
1082
1086
  if (deps === null) return [];
1083
- const installed = new Set([...Object.keys(deps.dependencies), ...Object.keys(deps.devDependencies)]);
1087
+ const installed = /* @__PURE__ */ new Set([...Object.keys(deps.dependencies), ...Object.keys(deps.devDependencies)]);
1084
1088
  return RECOMMENDED_GROUPS.filter((group) => !group.candidates.some((name) => installed.has(name))).map((group, index) => ({
1085
1089
  ruleId: RULE_ID$8,
1086
1090
  file: projectInfo.packageJsonPath,
@@ -1591,9 +1595,10 @@ const NUXT_PLUGIN_RULES = {
1591
1595
  "nuxt-doctor/server-routes/createError-on-failure": "warn",
1592
1596
  "nuxt-doctor/hydration/no-document-in-setup": "error",
1593
1597
  "nuxt-doctor/hydration/clientOnly-for-browser-apis": "error",
1598
+ "nuxt-doctor/hydration/no-browser-global-in-computed": "error",
1594
1599
  "nuxt-doctor/security/no-user-input-in-fetch-url": "warn"
1595
1600
  };
1596
- const VUE_OXLINT_RULE_IDS = new Set([
1601
+ const VUE_OXLINT_RULE_IDS = /* @__PURE__ */ new Set([
1597
1602
  "vue/no-export-in-script-setup",
1598
1603
  "vue/require-typed-ref",
1599
1604
  "vue/no-arrow-functions-in-watch",
@@ -1626,9 +1631,12 @@ const VUE_OXLINT_RULE_IDS = new Set([
1626
1631
  "vue-doctor/reactivity/prefer-shallowRef-for-large-data",
1627
1632
  "vue-doctor/reactivity/prefer-readonly-for-injected",
1628
1633
  "vue-doctor/reactivity/no-fresh-deps-in-watch",
1634
+ "vue-doctor/reactivity/no-stale-timer-ref",
1635
+ "vue-doctor/reactivity/effect-listener-cleanup-mismatch",
1629
1636
  "vue-doctor/composition/prefer-script-setup-for-new-files",
1630
1637
  "vue-doctor/composition/defineProps-typed",
1631
1638
  "vue-doctor/composition/no-pinia-store-in-setup",
1639
+ "vue-doctor/composition/no-prop-callback-in-setup",
1632
1640
  "vue-doctor/performance/prefer-defineAsyncComponent-on-route",
1633
1641
  "vue-doctor/performance/prefer-module-scope-static-value",
1634
1642
  "vue-doctor/performance/prefer-module-scope-pure-function",
@@ -1641,7 +1649,7 @@ const VUE_OXLINT_RULE_IDS = new Set([
1641
1649
  const NUXT_OXLINT_RULE_IDS = new Set(Object.keys(NUXT_PLUGIN_RULES));
1642
1650
  function oxlintRuleAllowlist(framework) {
1643
1651
  if (framework !== "nuxt") return VUE_OXLINT_RULE_IDS;
1644
- return new Set([...VUE_OXLINT_RULE_IDS, ...NUXT_OXLINT_RULE_IDS]);
1652
+ return /* @__PURE__ */ new Set([...VUE_OXLINT_RULE_IDS, ...NUXT_OXLINT_RULE_IDS]);
1645
1653
  }
1646
1654
  function toOxlintSeverity(s) {
1647
1655
  if (s === "error") return "error";
@@ -2158,6 +2166,20 @@ const RULE_REGISTRY = [
2158
2166
  source: "doctor",
2159
2167
  recommended: true
2160
2168
  },
2169
+ {
2170
+ id: "vue-doctor/reactivity/no-stale-timer-ref",
2171
+ severity: "warn",
2172
+ category: "reactivity",
2173
+ source: "doctor",
2174
+ recommended: true
2175
+ },
2176
+ {
2177
+ id: "vue-doctor/reactivity/effect-listener-cleanup-mismatch",
2178
+ severity: "warn",
2179
+ category: "reactivity",
2180
+ source: "doctor",
2181
+ recommended: true
2182
+ },
2161
2183
  {
2162
2184
  id: "vue-doctor/composition/prefer-script-setup-for-new-files",
2163
2185
  severity: "warn",
@@ -2179,6 +2201,13 @@ const RULE_REGISTRY = [
2179
2201
  source: "doctor",
2180
2202
  recommended: true
2181
2203
  },
2204
+ {
2205
+ id: "vue-doctor/composition/no-prop-callback-in-setup",
2206
+ severity: "warn",
2207
+ category: "composition",
2208
+ source: "doctor",
2209
+ recommended: true
2210
+ },
2182
2211
  {
2183
2212
  id: "vue-doctor/performance/prefer-defineAsyncComponent-on-route",
2184
2213
  severity: "info",
@@ -2536,6 +2565,13 @@ const RULE_REGISTRY = [
2536
2565
  source: "doctor",
2537
2566
  recommended: true
2538
2567
  },
2568
+ {
2569
+ id: "nuxt-doctor/hydration/no-browser-global-in-computed",
2570
+ severity: "error",
2571
+ category: "hydration",
2572
+ source: "doctor",
2573
+ recommended: true
2574
+ },
2539
2575
  {
2540
2576
  id: "nuxt-doctor/hydration/clientOnly-for-browser-apis",
2541
2577
  severity: "error",
@@ -2833,7 +2869,7 @@ async function parseSfcDescriptor(absPath) {
2833
2869
  const RULE_ID$5 = "vue-doctor/sfc/no-mixed-options-and-composition-api";
2834
2870
  const MESSAGE$5 = "Mixed Options API in a <script setup> SFC. Move data/methods/computed/watch/lifecycle into <script setup> Composition API; keep <script> only for options like name/inheritAttrs. See https://vuejs.org/api/sfc-script-setup.html#usage-alongside-normal-script";
2835
2871
  const RECOMMENDATION$5 = "Move this option into the <script setup> block using the Composition API, or keep <script> only for options-only config such as name or inheritAttrs.";
2836
- const DISALLOWED = new Set([
2872
+ const DISALLOWED = /* @__PURE__ */ new Set([
2837
2873
  "data",
2838
2874
  "methods",
2839
2875
  "computed",
@@ -3014,7 +3050,7 @@ function check$20(ctx) {
3014
3050
  const RULE_ID$3 = "nuxt-doctor/seo/og-image-defined";
3015
3051
  const MESSAGE$3 = "Page uses SEO meta but has no og:image property. Open Graph images improve social sharing previews. Add an og:image value or install @nuxtjs/og-image for automatic OG images.";
3016
3052
  const RECOMMENDATION$3 = "Add ogImage: \"/path/to/image.png\" to useSeoMeta / useHead, or install @nuxtjs/og-image.";
3017
- const SEO_PRIMITIVES$1 = new Set(["useSeoMeta", "useHead"]);
3053
+ const SEO_PRIMITIVES$1 = /* @__PURE__ */ new Set(["useSeoMeta", "useHead"]);
3018
3054
  function isSeoWrapperName$1(name) {
3019
3055
  if (SEO_PRIMITIVES$1.has(name)) return false;
3020
3056
  return /^use[a-z]*seo/i.test(name);
@@ -3080,7 +3116,7 @@ function check$19(ctx) {
3080
3116
  const RULE_ID$2 = "nuxt-doctor/seo/useSeoMeta-on-public-page";
3081
3117
  const MESSAGE$2 = "Public page component is missing SEO metadata. Add useSeoMeta, useHead, or definePageMeta with a title so search engines can index it properly.";
3082
3118
  const RECOMMENDATION$2 = "Call useSeoMeta({ title: \"...\" }) in<script setup> to define page title and meta tags for search engines and social previews.";
3083
- const SEO_PRIMITIVES = new Set([
3119
+ const SEO_PRIMITIVES = /* @__PURE__ */ new Set([
3084
3120
  "useSeoMeta",
3085
3121
  "useHead",
3086
3122
  "definePageMeta"
@@ -3273,7 +3309,7 @@ function ruleNoSharedKeyAcrossPages(pages) {
3273
3309
  }
3274
3310
  return diags;
3275
3311
  }
3276
- const BROWSER_GLOBALS = new Set([
3312
+ const BROWSER_GLOBALS = /* @__PURE__ */ new Set([
3277
3313
  "window",
3278
3314
  "document",
3279
3315
  "navigator",
@@ -3317,7 +3353,7 @@ function ruleSsrSafeOnMountedOnlyForClient(pages) {
3317
3353
  }
3318
3354
  return diags;
3319
3355
  }
3320
- const CLIENT_SAFE_WRAPPERS = new Set([
3356
+ const CLIENT_SAFE_WRAPPERS = /* @__PURE__ */ new Set([
3321
3357
  "onMounted",
3322
3358
  "watchEffect",
3323
3359
  "watch"
@@ -3753,7 +3789,7 @@ function check$8(ctx) {
3753
3789
  //#endregion
3754
3790
  //#region src/template/rules/no-javascript-uri.ts
3755
3791
  const NODE_ATTRIBUTE$1 = 6;
3756
- const URI_SINKS = new Set([
3792
+ const URI_SINKS = /* @__PURE__ */ new Set([
3757
3793
  "href",
3758
3794
  "src",
3759
3795
  "srcset",
@@ -3986,7 +4022,7 @@ function check$2(ctx) {
3986
4022
  }
3987
4023
  //#endregion
3988
4024
  //#region src/template/rules/no-missing-alt.ts
3989
- const IMAGE_TAGS = new Set([
4025
+ const IMAGE_TAGS = /* @__PURE__ */ new Set([
3990
4026
  "img",
3991
4027
  "NuxtImg",
3992
4028
  "nuxt-img"
@@ -4614,7 +4650,8 @@ async function loadDoctorConfig(rootDir, explicitPathOrOptions) {
4614
4650
  source = "flag";
4615
4651
  configFile = resolve(rootDir, explicitPath);
4616
4652
  } else if (result._configFile) {
4617
- source = SOURCE_MAP[extname(result._configFile).slice(1)];
4653
+ const ext = extname(result._configFile).slice(1);
4654
+ source = SOURCE_MAP[ext];
4618
4655
  configFile = result._configFile;
4619
4656
  } else if (Object.keys(raw).length > 0) {
4620
4657
  source = "package.json";
@@ -4932,6 +4969,7 @@ async function listChangedFiles(options) {
4932
4969
  "diff",
4933
4970
  "--name-only",
4934
4971
  "--cached",
4972
+ "--relative",
4935
4973
  "--diff-filter=ACMR"
4936
4974
  ]);
4937
4975
  else {
@@ -4939,6 +4977,7 @@ async function listChangedFiles(options) {
4939
4977
  "diff",
4940
4978
  "--name-only",
4941
4979
  "HEAD",
4980
+ "--relative",
4942
4981
  "--diff-filter=ACMR"
4943
4982
  ]);
4944
4983
  const untracked = await gitLines(rootDir, [