@decocms/start 2.10.0 → 2.12.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.
@@ -485,3 +485,41 @@ This script handles **Phases 0-6** of the [migration playbook](../deco-to-tansta
485
485
  - Phase 7-12 — Section registry tuning, route customization, matchers, async rendering, search
486
486
 
487
487
  The script gets you from "raw Fresh site" to "builds with `npm run build` and has ~0 old imports". Human work starts at runtime debugging and feature wiring.
488
+
489
+ ## Post-Migration Audit (`deco-post-cleanup`)
490
+
491
+ After the migration script's compile phase passes, run the
492
+ **`deco-post-cleanup`** audit to catch the residual cleanup the
493
+ script leaves behind on existing-but-pre-framework-helpers sites:
494
+
495
+ ```bash
496
+ # Read-only audit (default)
497
+ npx -p @decocms/start deco-post-cleanup
498
+
499
+ # Auto-fix the safe rules (dead-lib-shims, dead-runtime-shim, local-widgets-types)
500
+ npx -p @decocms/start deco-post-cleanup --fix
501
+
502
+ # CI gate: auto-fix safe rules, exit 2 if any warnings remain
503
+ npx -p @decocms/start deco-post-cleanup --fix --strict
504
+ ```
505
+
506
+ The audit covers 7 rules (delete dead lib shims, drop obsolete inline
507
+ Vite plugins, delete dead `runtime.ts` invoke shim, delete site-local
508
+ `withSiteGlobals` wrapper, repoint `~/lib/vtex-*` shim regressions,
509
+ delete shadowed `widgets.ts`, surface orphan framework TODOs). The
510
+ detection logic mirrors the canonical checklist at
511
+ [`deco-to-tanstack-migration/references/post-migration-cleanup.md`](../deco-to-tanstack-migration/references/post-migration-cleanup.md).
512
+
513
+ **Why `compile` and `audit` are complementary:**
514
+
515
+ | Tool | Catches |
516
+ |------|---------|
517
+ | `phase-compile` (in this script) | TS5097, missing exports, type bugs — anything `tsc --noEmit` finds |
518
+ | `deco-post-cleanup` (separate CLI) | Silent runtime stubs (e.g. dead `~/lib/vtex-*` shims that typecheck cleanly but resolve to `{}` at runtime) |
519
+
520
+ `tsc` doesn't catch the silent-stub class of bug because the dead
521
+ shim files have valid TypeScript signatures. The audit's pattern
522
+ matches surface what compilation cannot.
523
+
524
+ Source: `scripts/migrate-post-cleanup.ts` + `scripts/migrate/post-cleanup/`.
525
+ Tests: `scripts/migrate/post-cleanup/runner.test.ts`.
@@ -5,6 +5,43 @@ recurring set of dead-code and boilerplate cleanup that every migrated
5
5
  site benefits from. Run this checklist before the first PR review, not
6
6
  after the site has been shipping for weeks.
7
7
 
8
+ ## Run the audit first
9
+
10
+ This whole checklist is now automated by the **`deco-post-cleanup`**
11
+ audit script (added in `@decocms/start >= 2.11.0`, `--fix` mode in
12
+ `>= 2.12.0`). Run it from the site repo to get a structured report
13
+ of which sections below actually apply to your codebase:
14
+
15
+ ```bash
16
+ # Pretty text output, exits 0 unless --strict is passed
17
+ npx -p @decocms/start deco-post-cleanup
18
+
19
+ # Auto-apply mechanical fixes for the safe rules, then report what's left.
20
+ # Safe rules: dead-lib-shims, dead-runtime-shim, local-widgets-types.
21
+ # Other rules stay detect-only — they require human judgment.
22
+ npx -p @decocms/start deco-post-cleanup --fix
23
+
24
+ # Combine for CI: auto-fix safe rules, fail (exit 2) if warnings remain.
25
+ npx -p @decocms/start deco-post-cleanup --fix --strict
26
+
27
+ # Machine-readable JSON for dashboards
28
+ npx -p @decocms/start deco-post-cleanup --json
29
+ ```
30
+
31
+ The audit covers all 7 rules below and prints the exact file path +
32
+ suggested fix for each finding. With `--fix`, the three safe rules
33
+ auto-apply (`rm` for dead files, regex-anchored import rewrites for
34
+ shadowed shims). The output explicitly tags rules that require manual
35
+ work as `(0 fixed, manual)`, so you always know what's left after
36
+ auto-fix runs.
37
+
38
+ Real-world signal: on baggagio, `--fix` produced a byte-identical
39
+ diff to the manual cleanup PR a human had just made (45 files,
40
+ +45/-53). On casaevideo-storefront (production), the audit caught
41
+ six silent VTEX shim regressions that no `tsc --noEmit` run can
42
+ detect — those still require manual cleanup until rule 5 gains a
43
+ per-shim mapping table.
44
+
8
45
  ## 1. Delete unused `src/lib/*` shims
9
46
 
10
47
  The migration script's `templates/lib-utils.ts` generates 11 shim files
@@ -5,6 +5,43 @@ recurring set of dead-code and boilerplate cleanup that every migrated
5
5
  site benefits from. Run this checklist before the first PR review, not
6
6
  after the site has been shipping for weeks.
7
7
 
8
+ ## Run the audit first
9
+
10
+ This whole checklist is now automated by the **`deco-post-cleanup`**
11
+ audit script (added in `@decocms/start >= 2.11.0`, `--fix` mode in
12
+ `>= 2.12.0`). Run it from the site repo to get a structured report
13
+ of which sections below actually apply to your codebase:
14
+
15
+ ```bash
16
+ # Pretty text output, exits 0 unless --strict is passed
17
+ npx -p @decocms/start deco-post-cleanup
18
+
19
+ # Auto-apply mechanical fixes for the safe rules, then report what's left.
20
+ # Safe rules: dead-lib-shims, dead-runtime-shim, local-widgets-types.
21
+ # Other rules stay detect-only — they require human judgment.
22
+ npx -p @decocms/start deco-post-cleanup --fix
23
+
24
+ # Combine for CI: auto-fix safe rules, fail (exit 2) if warnings remain.
25
+ npx -p @decocms/start deco-post-cleanup --fix --strict
26
+
27
+ # Machine-readable JSON for dashboards
28
+ npx -p @decocms/start deco-post-cleanup --json
29
+ ```
30
+
31
+ The audit covers all 7 rules below and prints the exact file path +
32
+ suggested fix for each finding. With `--fix`, the three safe rules
33
+ auto-apply (`rm` for dead files, regex-anchored import rewrites for
34
+ shadowed shims). The output explicitly tags rules that require manual
35
+ work as `(0 fixed, manual)`, so you always know what's left after
36
+ auto-fix runs.
37
+
38
+ Real-world signal: on baggagio, `--fix` produced a byte-identical
39
+ diff to the manual cleanup PR a human had just made (45 files,
40
+ +45/-53). On casaevideo-storefront (production), the audit caught
41
+ six silent VTEX shim regressions that no `tsc --noEmit` run can
42
+ detect — those still require manual cleanup until rule 5 gains a
43
+ per-shim mapping table.
44
+
8
45
  ## 1. Delete unused `src/lib/*` shims
9
46
 
10
47
  The migration script's `templates/lib-utils.ts` generates 11 shim files
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@decocms/start",
3
- "version": "2.10.0",
3
+ "version": "2.12.0",
4
4
  "type": "module",
5
5
  "description": "Deco framework for TanStack Start - CMS bridge, admin protocol, hooks, schema generation",
6
6
  "main": "./src/index.ts",
7
7
  "bin": {
8
- "deco-migrate": "./scripts/migrate.ts"
8
+ "deco-migrate": "./scripts/migrate.ts",
9
+ "deco-post-cleanup": "./scripts/migrate-post-cleanup.ts"
9
10
  },
10
11
  "exports": {
11
12
  ".": "./src/index.ts",
@@ -59,6 +60,7 @@
59
60
  "./scripts/generate-schema": "./scripts/generate-schema.ts",
60
61
  "./scripts/generate-invoke": "./scripts/generate-invoke.ts",
61
62
  "./scripts/migrate": "./scripts/migrate.ts",
63
+ "./scripts/migrate-post-cleanup": "./scripts/migrate-post-cleanup.ts",
62
64
  "./scripts/tailwind-lint": "./scripts/tailwind-lint.ts",
63
65
  "./vite": "./src/vite/plugin.js",
64
66
  "./daemon": "./src/daemon/index.ts"
@@ -0,0 +1,371 @@
1
+ /**
2
+ * Post-migration cleanup audit — rule implementations.
3
+ *
4
+ * Each rule mirrors a section in
5
+ * `.agents/skills/deco-to-tanstack-migration/references/post-migration-cleanup.md`.
6
+ * The intent is to take the human checklist and make it programmatically
7
+ * detectable so future migrations get the same scrubbing automatically.
8
+ *
9
+ * Rules are intentionally read-only here — `--fix` is a follow-up.
10
+ */
11
+
12
+ import type { Finding, FixAction, FsWriter, Rule, RuleContext } from "./types";
13
+
14
+ const SRC_GLOB_EXCLUDES = ["node_modules", "dist", ".wrangler", ".vite", ".tanstack", "build"];
15
+
16
+ /**
17
+ * Rewrite all `from "<oldSpec>"` (or `from '<oldSpec>'`) imports in
18
+ * `src/**` to `from "<newSpec>"`. Returns the list of site-relative
19
+ * paths actually changed so fix-action summaries can quote a count.
20
+ * Uses the write side of the FS adapter — never touches disk in unit
21
+ * tests.
22
+ *
23
+ * Intentionally string-anchored on the exact spec; will not pick up
24
+ * partial-prefix matches like `~/types/widgets-extra`.
25
+ */
26
+ function rewriteImportSpec(
27
+ ctx: RuleContext,
28
+ writer: FsWriter,
29
+ oldSpec: string,
30
+ newSpec: string,
31
+ ): string[] {
32
+ const { siteDir, fs } = ctx;
33
+ const tsFiles = fs.glob(siteDir, "src/**/*.{ts,tsx}", SRC_GLOB_EXCLUDES);
34
+ const escaped = oldSpec.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
35
+ const re = new RegExp(`from\\s+(['"])${escaped}\\1`, "g");
36
+ const updated: string[] = [];
37
+ for (const abs of tsFiles) {
38
+ const content = fs.readText(abs);
39
+ if (!re.test(content)) {
40
+ re.lastIndex = 0;
41
+ continue;
42
+ }
43
+ re.lastIndex = 0;
44
+ const next = content.replace(re, (_m, q) => `from ${q}${newSpec}${q}`);
45
+ if (next !== content) {
46
+ writer.writeText(abs, next);
47
+ updated.push(abs.slice(siteDir.length + 1));
48
+ }
49
+ }
50
+ return updated;
51
+ }
52
+
53
+ /* ------------------------------------------------------------------ */
54
+ /* Rule 1 — dead `src/lib/*` shims */
55
+ /* ------------------------------------------------------------------ */
56
+
57
+ const EXPORT_RE = /^export\s+(?:function|const|interface|type|class)\s+([A-Za-z_][A-Za-z0-9_]*)/gm;
58
+
59
+ function extractExports(content: string): string[] {
60
+ const out: string[] = [];
61
+ for (const m of content.matchAll(EXPORT_RE)) {
62
+ out.push(m[1]);
63
+ }
64
+ return out;
65
+ }
66
+
67
+ function symbolUsedOutsideLib(siteDir: string, fs: RuleContext["fs"], symbol: string): boolean {
68
+ const tsFiles = fs.glob(siteDir, "src/**/*.{ts,tsx}", SRC_GLOB_EXCLUDES);
69
+ const re = new RegExp(`\\b${symbol}\\b`);
70
+ for (const file of tsFiles) {
71
+ if (file.includes("/src/lib/")) continue;
72
+ const content = fs.readText(file);
73
+ if (re.test(content)) return true;
74
+ }
75
+ return false;
76
+ }
77
+
78
+ const ruleDeadLibShims: Rule = {
79
+ id: "dead-lib-shims",
80
+ title: "Dead src/lib/* shims",
81
+ run({ siteDir, fs }: RuleContext): Finding[] {
82
+ const libFiles = fs.glob(siteDir, "src/lib/*.ts", SRC_GLOB_EXCLUDES);
83
+ if (libFiles.length === 0) return [];
84
+
85
+ const findings: Finding[] = [];
86
+ for (const abs of libFiles) {
87
+ const rel = abs.slice(siteDir.length + 1);
88
+ const content = fs.readText(abs);
89
+ const exports = extractExports(content);
90
+ if (exports.length === 0) continue;
91
+ const allDead = exports.every((s) => !symbolUsedOutsideLib(siteDir, fs, s));
92
+ if (!allDead) continue;
93
+ findings.push({
94
+ rule: "dead-lib-shims",
95
+ severity: "info",
96
+ file: rel,
97
+ message: `${exports.length} export(s), 0 external imports`,
98
+ fix: `rm ${rel}`,
99
+ meta: { exports },
100
+ });
101
+ }
102
+ return findings;
103
+ },
104
+ applyFix({ siteDir }, findings, writer): FixAction[] {
105
+ const actions: FixAction[] = [];
106
+ for (const f of findings) {
107
+ writer.deleteFile(`${siteDir}/${f.file}`);
108
+ actions.push({
109
+ file: f.file,
110
+ kind: "delete",
111
+ detail: "deleted (all exports verified unused)",
112
+ });
113
+ }
114
+ return actions;
115
+ },
116
+ };
117
+
118
+ /* ------------------------------------------------------------------ */
119
+ /* Rule 2 — obsolete inline vite plugins */
120
+ /* ------------------------------------------------------------------ */
121
+
122
+ const OBSOLETE_VITE_PLUGINS: { name: string; reason: string }[] = [
123
+ {
124
+ name: "site-manual-chunks",
125
+ reason: "framework's decoVitePlugin() now owns chunking",
126
+ },
127
+ {
128
+ name: "deco-stub-meta-gen",
129
+ reason: "framework now stubs meta.gen.{json,ts} on the client by default",
130
+ },
131
+ ];
132
+
133
+ const ruleObsoleteVitePlugins: Rule = {
134
+ id: "obsolete-vite-plugins",
135
+ title: "Obsolete inline Vite plugins",
136
+ run({ siteDir, fs }: RuleContext): Finding[] {
137
+ const findings: Finding[] = [];
138
+ const candidates = ["vite.config.ts", "vite.config.js", "vite.config.mjs"];
139
+ for (const rel of candidates) {
140
+ const abs = `${siteDir}/${rel}`;
141
+ if (!fs.exists(abs)) continue;
142
+ const content = fs.readText(abs);
143
+ for (const plugin of OBSOLETE_VITE_PLUGINS) {
144
+ const re = new RegExp(`name:\\s*["']${plugin.name}["']`);
145
+ if (!re.test(content)) continue;
146
+ findings.push({
147
+ rule: "obsolete-vite-plugins",
148
+ severity: "warning",
149
+ file: rel,
150
+ message: `'${plugin.name}' plugin is obsolete — ${plugin.reason}`,
151
+ fix: `delete the inline '${plugin.name}' plugin from ${rel}`,
152
+ meta: { plugin: plugin.name },
153
+ });
154
+ }
155
+ }
156
+ return findings;
157
+ },
158
+ };
159
+
160
+ /* ------------------------------------------------------------------ */
161
+ /* Rule 3 — dead `src/runtime.ts` invoke shim */
162
+ /* ------------------------------------------------------------------ */
163
+
164
+ const ruleDeadRuntimeShim: Rule = {
165
+ id: "dead-runtime-shim",
166
+ title: "Dead src/runtime.ts invoke shim",
167
+ run({ siteDir, fs }: RuleContext): Finding[] {
168
+ const abs = `${siteDir}/src/runtime.ts`;
169
+ if (!fs.exists(abs)) return [];
170
+ const content = fs.readText(abs);
171
+ // Heuristic: if the file's only meaningful exports are `invoke` /
172
+ // `createNestedInvokeProxy`, it's purely a shim.
173
+ const exports = extractExports(content);
174
+ const onlyInvokeShim =
175
+ exports.length > 0 && exports.every((e) => ["invoke", "createNestedInvokeProxy"].includes(e));
176
+ if (!onlyInvokeShim) return [];
177
+ return [
178
+ {
179
+ rule: "dead-runtime-shim",
180
+ severity: "info",
181
+ file: "src/runtime.ts",
182
+ message: `Only re-exports invoke (${exports.join(", ")}) — replace with @decocms/start/sdk`,
183
+ fix: 'rg -l "from \\"~/runtime\\"" src/ | xargs sed -i \'\' \'s|from "~/runtime"|from "@decocms/start/sdk"|g\' && rm src/runtime.ts',
184
+ },
185
+ ];
186
+ },
187
+ applyFix(ctx, findings, writer): FixAction[] {
188
+ if (findings.length === 0) return [];
189
+ const updated = rewriteImportSpec(ctx, writer, "~/runtime", "@decocms/start/sdk");
190
+ writer.deleteFile(`${ctx.siteDir}/src/runtime.ts`);
191
+ return [
192
+ {
193
+ file: "src/runtime.ts",
194
+ kind: "rewrite-imports+delete",
195
+ detail: `rewrote ${updated.length} import(s) → @decocms/start/sdk and deleted src/runtime.ts`,
196
+ },
197
+ ];
198
+ },
199
+ };
200
+
201
+ /* ------------------------------------------------------------------ */
202
+ /* Rule 4 — site-local `withSiteGlobals` workaround */
203
+ /* ------------------------------------------------------------------ */
204
+
205
+ const ruleSiteLocalGlobals: Rule = {
206
+ id: "site-local-with-globals",
207
+ title: "Site-local withSiteGlobals wrapper",
208
+ run({ siteDir, fs }: RuleContext): Finding[] {
209
+ const findings: Finding[] = [];
210
+ const candidates = fs.glob(siteDir, "src/**/withSiteGlobals.ts", SRC_GLOB_EXCLUDES);
211
+ for (const abs of candidates) {
212
+ const content = fs.readText(abs);
213
+ // Heuristic: any local definition (function/const) of withSiteGlobals or
214
+ // cmsRouteWithGlobals indicates a local wrapper, not a re-export from
215
+ // the framework. The framework version would just re-export.
216
+ const definesWrapper =
217
+ /(?:export\s+)?(?:function|const)\s+(?:withSiteGlobals|cmsRouteWithGlobals)\b/.test(
218
+ content,
219
+ );
220
+ const reExportsFromFramework = /from\s+['"]@decocms\/start\/routes['"]/.test(content);
221
+ if (!definesWrapper || reExportsFromFramework) continue;
222
+ const rel = abs.slice(siteDir.length + 1);
223
+ const lineCount = content.split("\n").length;
224
+ findings.push({
225
+ rule: "site-local-with-globals",
226
+ severity: "warning",
227
+ file: rel,
228
+ message: `Local wrapper (~${lineCount} LOC) — framework now exports withSiteGlobals from @decocms/start/routes`,
229
+ fix: "delete the local wrapper and import { withSiteGlobals } from '@decocms/start/routes'",
230
+ meta: { lineCount },
231
+ });
232
+ }
233
+ return findings;
234
+ },
235
+ };
236
+
237
+ /* ------------------------------------------------------------------ */
238
+ /* Rule 5 — `~/lib/vtex-*` shim regression */
239
+ /* ------------------------------------------------------------------ */
240
+
241
+ const ruleVtexShimRegression: Rule = {
242
+ id: "vtex-shim-regression",
243
+ title: "Imports from ~/lib/vtex-* (silent stub regression)",
244
+ run({ siteDir, fs }: RuleContext): Finding[] {
245
+ const tsFiles = fs.glob(siteDir, "src/**/*.{ts,tsx}", SRC_GLOB_EXCLUDES);
246
+ const findings: Finding[] = [];
247
+ const re = /from\s+['"]~\/lib\/vtex-([A-Za-z0-9-]+)['"]/g;
248
+ for (const abs of tsFiles) {
249
+ if (abs.includes("/src/lib/")) continue;
250
+ const content = fs.readText(abs);
251
+ const matches = [...content.matchAll(re)];
252
+ if (matches.length === 0) continue;
253
+ const rel = abs.slice(siteDir.length + 1);
254
+ const shims = [...new Set(matches.map((m) => `vtex-${m[1]}`))];
255
+ findings.push({
256
+ rule: "vtex-shim-regression",
257
+ severity: "warning",
258
+ file: rel,
259
+ message: `Imports from dead shim(s): ${shims.join(", ")} — runtime is silently stubbed`,
260
+ fix: "Repoint imports to '@decocms/apps/vtex/...' or 'apps/commerce/utils/...'",
261
+ meta: { shims },
262
+ });
263
+ }
264
+ return findings;
265
+ },
266
+ };
267
+
268
+ /* ------------------------------------------------------------------ */
269
+ /* Rule 6 — local `src/types/widgets.ts` shadowing framework */
270
+ /* ------------------------------------------------------------------ */
271
+
272
+ const ruleLocalWidgetsTypes: Rule = {
273
+ id: "local-widgets-types",
274
+ title: "Local src/types/widgets.ts shadowing framework",
275
+ run({ siteDir, fs }: RuleContext): Finding[] {
276
+ const abs = `${siteDir}/src/types/widgets.ts`;
277
+ if (!fs.exists(abs)) return [];
278
+ const tsFiles = fs.glob(siteDir, "src/**/*.{ts,tsx}", SRC_GLOB_EXCLUDES);
279
+ const re = /from\s+['"]~\/types\/widgets['"]/;
280
+ let importCount = 0;
281
+ for (const f of tsFiles) {
282
+ if (f === abs) continue;
283
+ if (re.test(fs.readText(f))) importCount++;
284
+ }
285
+ return [
286
+ {
287
+ rule: "local-widgets-types",
288
+ severity: "info",
289
+ file: "src/types/widgets.ts",
290
+ message: `Local file shadows @decocms/start/types/widgets (used in ${importCount} place(s))`,
291
+ fix: 'rewrite imports to "@decocms/start/types/widgets" and rm src/types/widgets.ts',
292
+ meta: { importCount },
293
+ },
294
+ ];
295
+ },
296
+ applyFix(ctx, findings, writer): FixAction[] {
297
+ if (findings.length === 0) return [];
298
+ const updated = rewriteImportSpec(
299
+ ctx,
300
+ writer,
301
+ "~/types/widgets",
302
+ "@decocms/start/types/widgets",
303
+ );
304
+ writer.deleteFile(`${ctx.siteDir}/src/types/widgets.ts`);
305
+ return [
306
+ {
307
+ file: "src/types/widgets.ts",
308
+ kind: "rewrite-imports+delete",
309
+ detail: `rewrote ${updated.length} import(s) → @decocms/start/types/widgets and deleted src/types/widgets.ts`,
310
+ },
311
+ ];
312
+ },
313
+ };
314
+
315
+ /* ------------------------------------------------------------------ */
316
+ /* Rule 7 — orphan "TODO: framework" comments */
317
+ /* ------------------------------------------------------------------ */
318
+
319
+ const ruleFrameworkTodos: Rule = {
320
+ id: "framework-todos",
321
+ title: "Orphan TODOs deferring to the framework",
322
+ run({ siteDir, fs }: RuleContext): Finding[] {
323
+ const tsFiles = [
324
+ ...fs.glob(siteDir, "src/**/*.{ts,tsx}", SRC_GLOB_EXCLUDES),
325
+ ...fs.glob(siteDir, "vite.config.ts", SRC_GLOB_EXCLUDES),
326
+ ];
327
+ const findings: Finding[] = [];
328
+ const re = /TODO[^\n]*?(?:deco|framework|move into)/i;
329
+ for (const abs of tsFiles) {
330
+ const content = fs.readText(abs);
331
+ const lines = content.split("\n");
332
+ for (let i = 0; i < lines.length; i++) {
333
+ if (!re.test(lines[i])) continue;
334
+ const rel = abs.slice(siteDir.length + 1);
335
+ findings.push({
336
+ rule: "framework-todos",
337
+ severity: "info",
338
+ file: `${rel}:${i + 1}`,
339
+ message: lines[i].trim().slice(0, 120),
340
+ fix: "Triage: shipped → adopt; deferred → file issue; obsolete → delete",
341
+ });
342
+ }
343
+ }
344
+ return findings;
345
+ },
346
+ };
347
+
348
+ export const ALL_RULES: Rule[] = [
349
+ ruleDeadLibShims,
350
+ ruleObsoleteVitePlugins,
351
+ ruleDeadRuntimeShim,
352
+ ruleSiteLocalGlobals,
353
+ ruleVtexShimRegression,
354
+ ruleLocalWidgetsTypes,
355
+ ruleFrameworkTodos,
356
+ ];
357
+
358
+ /** Exported for direct unit tests. */
359
+ export const _internals = {
360
+ extractExports,
361
+ symbolUsedOutsideLib,
362
+ rules: {
363
+ ruleDeadLibShims,
364
+ ruleObsoleteVitePlugins,
365
+ ruleDeadRuntimeShim,
366
+ ruleSiteLocalGlobals,
367
+ ruleVtexShimRegression,
368
+ ruleLocalWidgetsTypes,
369
+ ruleFrameworkTodos,
370
+ },
371
+ };