@superblocksteam/sdk 2.0.141-next.2 → 2.0.142-next.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.
Files changed (31) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/dev-utils/dev-server.optimize-deps.test.mjs +242 -2
  3. package/dist/dev-utils/dev-server.optimize-deps.test.mjs.map +1 -1
  4. package/dist/dev-utils/optimize-deps-config.d.mts +14 -0
  5. package/dist/dev-utils/optimize-deps-config.d.mts.map +1 -1
  6. package/dist/dev-utils/optimize-deps-config.mjs +104 -8
  7. package/dist/dev-utils/optimize-deps-config.mjs.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/telemetry/startup-context.d.ts +17 -0
  13. package/dist/telemetry/startup-context.d.ts.map +1 -0
  14. package/dist/telemetry/startup-context.js +43 -0
  15. package/dist/telemetry/startup-context.js.map +1 -0
  16. package/dist/telemetry/startup-context.test.d.ts +2 -0
  17. package/dist/telemetry/startup-context.test.d.ts.map +1 -0
  18. package/dist/telemetry/startup-context.test.js +70 -0
  19. package/dist/telemetry/startup-context.test.js.map +1 -0
  20. package/dist/vite-plugin-optimize-lucide-imports.d.ts +20 -0
  21. package/dist/vite-plugin-optimize-lucide-imports.d.ts.map +1 -1
  22. package/dist/vite-plugin-optimize-lucide-imports.js +19 -4
  23. package/dist/vite-plugin-optimize-lucide-imports.js.map +1 -1
  24. package/package.json +6 -6
  25. package/src/dev-utils/dev-server.optimize-deps.test.mts +334 -2
  26. package/src/dev-utils/optimize-deps-config.mts +106 -8
  27. package/src/index.ts +7 -0
  28. package/src/telemetry/startup-context.test.ts +102 -0
  29. package/src/telemetry/startup-context.ts +77 -0
  30. package/src/vite-plugin-optimize-lucide-imports.ts +21 -4
  31. package/tsconfig.tsbuildinfo +1 -1
@@ -0,0 +1,70 @@
1
+ import { ROOT_CONTEXT, trace } from "@opentelemetry/api";
2
+ import { afterAll, beforeAll, describe, expect, it } from "vitest";
3
+ import { getTestTelemetryInstance, getTestTracer, initTestTelemetry, resetTestTelemetry, } from "@superblocksteam/telemetry/testing";
4
+ import { applyStartupTraceCarrier, extractStartupTraceContext, startupTraceCarrierFromHeaders, withStartupTraceContext, } from "./startup-context.js";
5
+ const TRACE_ID = "4bf92f3577b34da6a3ce929d0e0e4736";
6
+ const TRACEPARENT = `00-${TRACE_ID}-00f067aa0ba902b7-01`;
7
+ describe("sdk-dev-server startup trace context", () => {
8
+ beforeAll(() => {
9
+ initTestTelemetry();
10
+ });
11
+ afterAll(async () => {
12
+ await resetTestTelemetry();
13
+ });
14
+ it("extracts the SABS parent from cold-start environment values", () => {
15
+ const parentContext = extractStartupTraceContext({
16
+ TRACEPARENT,
17
+ TRACESTATE: "vendor=value",
18
+ });
19
+ expect(trace.getSpanContext(parentContext)).toMatchObject({
20
+ isRemote: true,
21
+ traceId: TRACE_ID,
22
+ traceFlags: 1,
23
+ traceState: expect.objectContaining({
24
+ serialize: expect.any(Function),
25
+ }),
26
+ });
27
+ expect(trace.getSpanContext(parentContext)?.traceState?.serialize()).toBe("vendor=value");
28
+ });
29
+ it("uses the root context when startup propagation is absent", () => {
30
+ expect(extractStartupTraceContext({})).toBe(ROOT_CONTEXT);
31
+ });
32
+ it("stores warm activation headers for the startup path without stale tracestate", () => {
33
+ const env = {
34
+ TRACEPARENT: "stale",
35
+ TRACESTATE: "stale=value",
36
+ };
37
+ const carrier = { traceparent: TRACEPARENT };
38
+ applyStartupTraceCarrier(carrier, env);
39
+ expect(env.TRACEPARENT).toBe(TRACEPARENT);
40
+ expect(env.TRACESTATE).toBeUndefined();
41
+ });
42
+ it("captures inbound warm activation trace headers", () => {
43
+ expect(startupTraceCarrierFromHeaders({
44
+ traceparent: TRACEPARENT,
45
+ tracestate: "vendor=value",
46
+ })).toEqual({
47
+ traceparent: TRACEPARENT,
48
+ tracestate: "vendor=value",
49
+ });
50
+ });
51
+ it("parents the dev-server startup span to the propagated SABS context", async () => {
52
+ const environment = {
53
+ TRACEPARENT,
54
+ TRACESTATE: "vendor=value",
55
+ };
56
+ await withStartupTraceContext(environment, async () => {
57
+ await getTestTracer().startActiveSpan("devServerStartup", async (span) => {
58
+ span.end();
59
+ });
60
+ });
61
+ const [startupSpan] = getTestTelemetryInstance()
62
+ .spanExporter.getSpans()
63
+ .filter((span) => span.name === "devServerStartup");
64
+ expect(startupSpan.spanContext().traceId).toBe(TRACE_ID);
65
+ expect(startupSpan.parentSpanContext?.spanId).toBe("00f067aa0ba902b7");
66
+ expect(environment.TRACEPARENT).toBeUndefined();
67
+ expect(environment.TRACESTATE).toBeUndefined();
68
+ });
69
+ });
70
+ //# sourceMappingURL=startup-context.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"startup-context.test.js","sourceRoot":"","sources":["../../src/telemetry/startup-context.test.ts"],"names":[],"mappings":"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"}
@@ -4,5 +4,25 @@ import type { Plugin } from "vite";
4
4
  * destructured imports to direct imports for better tree-shaking
5
5
  */
6
6
  declare function createLucideReactImportOptimizer(): Plugin;
7
+ /**
8
+ * Transforms lucide-react imports from destructured to individual imports.
9
+ * Exported so tests can pin the emitted import specifiers against the ids the
10
+ * dep optimizer registers.
11
+ */
12
+ export declare function transformLucideImports(sourceCode: string): {
13
+ transformedCode: string;
14
+ hasChanges: boolean;
15
+ };
16
+ /**
17
+ * The module id this plugin emits for a named lucide-react import. The `.js`
18
+ * extension is load-bearing: the dep optimizer's include glob registers each
19
+ * icon under its on-disk id (`…/icons/check.js`), and Vite's
20
+ * `tryOptimizedResolve` matches optimized deps by EXACT id — an extensionless
21
+ * import misses the optimized entry and is re-registered as a newly discovered
22
+ * dependency, re-running the optimizer (the reload this plugin + config
23
+ * combination exists to prevent). Exported so tests can assert the emitted ids
24
+ * and the optimized ids stay identical.
25
+ */
26
+ export declare function lucideIconModuleId(importName: string): string;
7
27
  export default createLucideReactImportOptimizer;
8
28
  //# sourceMappingURL=vite-plugin-optimize-lucide-imports.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-optimize-lucide-imports.d.ts","sourceRoot":"","sources":["../src/vite-plugin-optimize-lucide-imports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,MAAM,EAAE,MAAM,MAAM,CAAC;AAMpD;;;GAGG;AACH,iBAAS,gCAAgC,IAAI,MAAM,CA8BlD;AAyFD,eAAe,gCAAgC,CAAC"}
1
+ {"version":3,"file":"vite-plugin-optimize-lucide-imports.d.ts","sourceRoot":"","sources":["../src/vite-plugin-optimize-lucide-imports.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,MAAM,EAAE,MAAM,MAAM,CAAC;AAQpD;;;GAGG;AACH,iBAAS,gCAAgC,IAAI,MAAM,CA8BlD;AASD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG;IAC1D,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,OAAO,CAAC;CACrB,CAyBA;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE7D;AAgDD,eAAe,gCAAgC,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { LUCIDE_ICON_DEEP_PATH_PREFIX } from "./dev-utils/optimize-deps-config.mjs";
1
2
  // Pre-compile regex for better performance
2
3
  const LUCIDE_IMPORT_PATTERN = /([ \t]*)import\s+\{\s*([^}]+)\s*\}\s+from\s+['"]lucide-react['"]/g;
3
4
  /**
@@ -37,9 +38,11 @@ function isValidInput(code, id) {
37
38
  return Boolean(code && id);
38
39
  }
39
40
  /**
40
- * Transforms lucide-react imports from destructured to individual imports
41
+ * Transforms lucide-react imports from destructured to individual imports.
42
+ * Exported so tests can pin the emitted import specifiers against the ids the
43
+ * dep optimizer registers.
41
44
  */
42
- function transformLucideImports(sourceCode) {
45
+ export function transformLucideImports(sourceCode) {
43
46
  let hasChanges = false;
44
47
  const transformedCode = sourceCode.replace(LUCIDE_IMPORT_PATTERN, (match, indentation, importNames) => {
45
48
  if (!importNames.trim())
@@ -54,6 +57,19 @@ function transformLucideImports(sourceCode) {
54
57
  });
55
58
  return { transformedCode, hasChanges };
56
59
  }
60
+ /**
61
+ * The module id this plugin emits for a named lucide-react import. The `.js`
62
+ * extension is load-bearing: the dep optimizer's include glob registers each
63
+ * icon under its on-disk id (`…/icons/check.js`), and Vite's
64
+ * `tryOptimizedResolve` matches optimized deps by EXACT id — an extensionless
65
+ * import misses the optimized entry and is re-registered as a newly discovered
66
+ * dependency, re-running the optimizer (the reload this plugin + config
67
+ * combination exists to prevent). Exported so tests can assert the emitted ids
68
+ * and the optimized ids stay identical.
69
+ */
70
+ export function lucideIconModuleId(importName) {
71
+ return `${LUCIDE_ICON_DEEP_PATH_PREFIX}${convertToKebabCase(importName)}.js`;
72
+ }
57
73
  /**
58
74
  * Converts a comma-separated list of imports to individual import statements
59
75
  */
@@ -63,9 +79,8 @@ function convertToIndividualImports(importNames, indentation, withSemicolon) {
63
79
  .map((name) => name.trim())
64
80
  .filter(Boolean)
65
81
  .map((name) => {
66
- const kebabCasePath = convertToKebabCase(name);
67
82
  const semicolon = withSemicolon ? ";" : "";
68
- return `${indentation}import ${name} from "lucide-react/dist/esm/icons/${kebabCasePath}"${semicolon}`;
83
+ return `${indentation}import ${name} from "${lucideIconModuleId(name)}"${semicolon}`;
69
84
  })
70
85
  .join("\n");
71
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"vite-plugin-optimize-lucide-imports.js","sourceRoot":"","sources":["../src/vite-plugin-optimize-lucide-imports.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"vite-plugin-optimize-lucide-imports.js","sourceRoot":"","sources":["../src/vite-plugin-optimize-lucide-imports.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/sdk",
3
- "version": "2.0.141-next.2",
3
+ "version": "2.0.142-next.0",
4
4
  "description": "Superblocks JS SDK",
5
5
  "homepage": "https://www.superblocks.com",
6
6
  "license": "Superblocks Community Software License",
@@ -57,11 +57,11 @@
57
57
  "vite-tsconfig-paths": "^6.0.4",
58
58
  "winston": "^3.17.0",
59
59
  "yaml": "^2.7.1",
60
- "@superblocksteam/library-shared": "2.0.141-next.2",
61
- "@superblocksteam/shared": "0.9598.2",
62
- "@superblocksteam/util": "2.0.141-next.2",
63
- "@superblocksteam/telemetry": "2.0.141-next.2",
64
- "@superblocksteam/vite-plugin-file-sync": "2.0.141-next.2"
60
+ "@superblocksteam/library-shared": "2.0.142-next.0",
61
+ "@superblocksteam/telemetry": "2.0.142-next.0",
62
+ "@superblocksteam/util": "2.0.142-next.0",
63
+ "@superblocksteam/vite-plugin-file-sync": "2.0.142-next.0",
64
+ "@superblocksteam/shared": "0.9599.0"
65
65
  },
66
66
  "devDependencies": {
67
67
  "@eslint/js": "^9.39.2",
@@ -1,6 +1,8 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises";
2
3
  import { tmpdir } from "node:os";
3
- import { join } from "node:path";
4
+ import { dirname, join } from "node:path";
5
+ import { fileURLToPath } from "node:url";
4
6
 
5
7
  import { optimizeDeps, resolveConfig } from "vite";
6
8
  import { afterAll, beforeAll, describe, expect, it } from "vitest";
@@ -11,7 +13,14 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest";
11
13
  // drag in the heavy `dev-server.mjs` graph (vite + express + file-sync), which
12
14
  // would make a transitive load failure surface as an opaque import error rather
13
15
  // than a meaningful assertion.
14
- import { OPTIMIZE_DEPS_CONFIG } from "./optimize-deps-config.mjs";
16
+ import {
17
+ lucideIconModuleId,
18
+ transformLucideImports,
19
+ } from "../vite-plugin-optimize-lucide-imports.js";
20
+ import {
21
+ NOT_PRE_OPTIMIZED_TEMPLATE_DEPS,
22
+ OPTIMIZE_DEPS_CONFIG,
23
+ } from "./optimize-deps-config.mjs";
15
24
 
16
25
  /**
17
26
  * Behavioral regression repro for APPS-4576.
@@ -669,3 +678,326 @@ describe("dev server optimizeDeps includes the JSX runtime and excludes the virt
669
678
  ).not.toContain(SDK_API);
670
679
  }, 30_000);
671
680
  });
681
+
682
+ /**
683
+ * Static pre-optimize contract for common live-edit template deps.
684
+ *
685
+ * The `entries` scan above pre-bundles deps that *existing* app source imports
686
+ * at pre-warm time. But a warm-pool box pre-warms against the seeded template,
687
+ * and Clark's generated code routinely imports template-declared deps the seed
688
+ * never touches (an unused Radix primitive, `recharts` for the first dashboard).
689
+ * Those first-time imports are runtime discoveries — a re-optimize, and in the
690
+ * worst case the "optimized dependencies changed. reloading" reload that drops
691
+ * the editor websocket. Force-including every dep the templates declare closes
692
+ * that gap.
693
+ *
694
+ * The list is a deliberate *static* duplicate of the template dependencies
695
+ * (not read from package.json at runtime): `include` IS part of Vite's dep
696
+ * cache config hash, so the pre-warm and activation servers must see the exact
697
+ * same value or activation discards the pre-warmed cache. The template-contract
698
+ * tests below keep the copy honest against both live-edit templates, in BOTH
699
+ * directions: everything included must be declared by both templates, and
700
+ * every dep both templates share must be included or explicitly excluded with
701
+ * a documented reason (NOT_PRE_OPTIMIZED_TEMPLATE_DEPS).
702
+ */
703
+ describe("dev server optimizeDeps pre-optimizes common live-edit template deps", () => {
704
+ // Change-detector copy: intentionally duplicated from OPTIMIZE_DEPS_CONFIG so
705
+ // an include-list edit must be made consciously in both places.
706
+ const EXPECTED_INCLUDE = [
707
+ "@radix-ui/react-accordion",
708
+ "@radix-ui/react-aspect-ratio",
709
+ "@radix-ui/react-avatar",
710
+ "@radix-ui/react-checkbox",
711
+ "@radix-ui/react-dialog",
712
+ "@radix-ui/react-dropdown-menu",
713
+ "@radix-ui/react-hover-card",
714
+ "@radix-ui/react-label",
715
+ "@radix-ui/react-navigation-menu",
716
+ "@radix-ui/react-popover",
717
+ "@radix-ui/react-progress",
718
+ "@radix-ui/react-scroll-area",
719
+ "@radix-ui/react-select",
720
+ "@radix-ui/react-separator",
721
+ "@radix-ui/react-slider",
722
+ "@radix-ui/react-slot",
723
+ "@radix-ui/react-switch",
724
+ "@radix-ui/react-tabs",
725
+ "@radix-ui/react-toggle",
726
+ "@radix-ui/react-toggle-group",
727
+ "@radix-ui/react-tooltip",
728
+ "@superblocksteam/library/jsx-dev-runtime",
729
+ "class-variance-authority",
730
+ "clsx",
731
+ "date-fns",
732
+ "lucide-react",
733
+ "lucide-react/dist/esm/icons/*",
734
+ "react",
735
+ "react-day-picker",
736
+ "react-dom",
737
+ "react-dom/client",
738
+ "react-dropzone",
739
+ "react-router",
740
+ "react-syntax-highlighter",
741
+ "react/jsx-runtime",
742
+ "recharts",
743
+ "sonner",
744
+ "tailwind-merge",
745
+ ];
746
+
747
+ it("force-includes the common template deps shared by pre-warm and activation", () => {
748
+ expect(OPTIMIZE_DEPS_CONFIG?.include).toEqual(EXPECTED_INCLUDE);
749
+ });
750
+
751
+ it("does not pre-bundle the bare Superblocks library runtime", () => {
752
+ // Pre-bundling `@superblocksteam/library` itself creates a second copy of
753
+ // the library runtime: generated apps also receive it through the SDK's
754
+ // plugin pipeline, and a prebundled duplicate splits React context/hook
755
+ // state between the two copies. Only the `/jsx-dev-runtime` subpath (which
756
+ // the scan structurally cannot discover — see the include/exclude suite
757
+ // above) is force-included.
758
+ expect(OPTIMIZE_DEPS_CONFIG?.include).not.toContain(
759
+ "@superblocksteam/library",
760
+ );
761
+ });
762
+
763
+ it("keeps the include list sorted for maintainability", () => {
764
+ expect(OPTIMIZE_DEPS_CONFIG?.include).toEqual(
765
+ [...(OPTIMIZE_DEPS_CONFIG?.include ?? [])].sort(),
766
+ );
767
+ });
768
+
769
+ it("retains the esbuild destructuring transform for optimized deps", () => {
770
+ expect(OPTIMIZE_DEPS_CONFIG?.esbuildOptions?.supported).toEqual({
771
+ destructuring: true,
772
+ });
773
+ });
774
+
775
+ const LIVE_EDIT_TEMPLATES = ["app-fullstack", "shadcn-demo-app"];
776
+
777
+ const packageNameForInclude = (dep: string) => {
778
+ const parts = dep.split("/");
779
+ return dep.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
780
+ };
781
+
782
+ const templatesRoot = join(
783
+ dirname(fileURLToPath(import.meta.url)),
784
+ "..",
785
+ "..",
786
+ "..",
787
+ "..",
788
+ "..",
789
+ "demo-apps",
790
+ );
791
+
792
+ // Validated read of a template's declared dependencies — no type assertion:
793
+ // a malformed package.json fails with a descriptive error, not a silent
794
+ // empty set.
795
+ function readTemplateDependencies(templateName: string): Set<string> {
796
+ const raw: unknown = JSON.parse(
797
+ readFileSync(join(templatesRoot, templateName, "package.json"), "utf8"),
798
+ );
799
+ if (raw === null || typeof raw !== "object" || !("dependencies" in raw)) {
800
+ throw new Error(
801
+ `"${templateName}" package.json has no dependencies object`,
802
+ );
803
+ }
804
+ const dependencies: unknown = raw.dependencies;
805
+ if (
806
+ dependencies === null ||
807
+ typeof dependencies !== "object" ||
808
+ Array.isArray(dependencies)
809
+ ) {
810
+ throw new Error(
811
+ `"${templateName}" package.json dependencies is not an object`,
812
+ );
813
+ }
814
+ return new Set(Object.keys(dependencies));
815
+ }
816
+
817
+ it("only includes deps that both live-edit templates declare", () => {
818
+ // Every force-included package must be installed in every app the config
819
+ // serves, or the include is a no-op for that app (Vite warns and skips an
820
+ // unresolvable entry). Both live-edit templates are the install contract:
821
+ // if a package leaves the templates, it must leave this list; if it is in
822
+ // this list, both templates must declare it.
823
+ const includePackages = new Set(
824
+ (OPTIMIZE_DEPS_CONFIG?.include ?? []).map(packageNameForInclude),
825
+ );
826
+
827
+ for (const templateName of LIVE_EDIT_TEMPLATES) {
828
+ const templateDependencies = readTemplateDependencies(templateName);
829
+
830
+ expect(
831
+ [...includePackages].filter((dep) => !templateDependencies.has(dep)),
832
+ `packages force-included by OPTIMIZE_DEPS_CONFIG but not declared as ` +
833
+ `dependencies of the "${templateName}" template`,
834
+ ).toEqual([]);
835
+ }
836
+ });
837
+
838
+ it("includes or explicitly excludes every dep the templates share", () => {
839
+ // Reverse direction of the contract above: the include list claims to be
840
+ // "every dep both templates declare", so a dep present in BOTH templates
841
+ // must either be force-included or appear in NOT_PRE_OPTIMIZED_TEMPLATE_DEPS
842
+ // with a documented reason. Without this, a newly-seeded template dep
843
+ // silently regresses to a runtime discovery on its first generated import.
844
+ const includePackages = new Set(
845
+ (OPTIMIZE_DEPS_CONFIG?.include ?? []).map(packageNameForInclude),
846
+ );
847
+ const excluded = new Set(NOT_PRE_OPTIMIZED_TEMPLATE_DEPS);
848
+
849
+ const [first, ...rest] = LIVE_EDIT_TEMPLATES.map(readTemplateDependencies);
850
+ const shared = [...first].filter((dep) =>
851
+ rest.every((deps) => deps.has(dep)),
852
+ );
853
+
854
+ expect(
855
+ shared.filter((dep) => !includePackages.has(dep) && !excluded.has(dep)),
856
+ `deps declared by ALL live-edit templates that are neither force-` +
857
+ `included nor documented in NOT_PRE_OPTIMIZED_TEMPLATE_DEPS`,
858
+ ).toEqual([]);
859
+
860
+ // Keep the exclusion list honest too: an entry that no longer exists in
861
+ // the shared template deps is stale and should be removed.
862
+ expect(
863
+ [...excluded].filter((dep) => !shared.includes(dep)),
864
+ `NOT_PRE_OPTIMIZED_TEMPLATE_DEPS entries no longer declared by all ` +
865
+ `live-edit templates`,
866
+ ).toEqual([]);
867
+ });
868
+ });
869
+
870
+ /**
871
+ * Regression repro for the lucide-react per-icon deep-path gap.
872
+ *
873
+ * `vite-plugin-optimize-lucide-imports` unconditionally rewrites
874
+ * `import { Check } from "lucide-react"` into
875
+ * `import Check from "lucide-react/dist/esm/icons/check.js"` at serve time.
876
+ * The dep optimizer therefore sees the per-icon DEEP paths, never the bare
877
+ * specifier — so `include: ["lucide-react"]` pre-bundles the package entry
878
+ * while every first-time icon import is still a runtime discovery. The config
879
+ * must include the icon glob (`lucide-react/dist/esm/icons/*`, expanded by
880
+ * Vite's `expandGlobIds`) so newly-generated code can use any icon without
881
+ * triggering a re-optimize. Measured against the real template's lucide-react
882
+ * (1,851 icons): the glob adds ~1.4s to cold-start optimize — inside the
883
+ * warm-standby budget, and it covers the whole icon set instead of a top-N
884
+ * guess.
885
+ *
886
+ * The exact id form matters: Vite's `tryOptimizedResolve` matches optimized
887
+ * deps by EXACT id (its only fallback loop handles nested `"parent > dep"`
888
+ * ids, not resolved files — verified against Vite 6.4.3). Glob expansion
889
+ * registers each icon under its on-disk id (`…/icons/check.js`), so the
890
+ * rewrite plugin must emit the same `.js`-suffixed ids or an extensionless
891
+ * import misses the optimized entry and re-registers the icon as a newly
892
+ * discovered dependency, re-running the optimizer anyway. The tests below pin
893
+ * both halves: the plugin's emitted ids and the glob's optimized ids are the
894
+ * same strings.
895
+ */
896
+ describe("dev server optimizeDeps pre-bundles lucide-react per-icon deep paths", () => {
897
+ let fixtureRoot: string;
898
+
899
+ beforeAll(async () => {
900
+ fixtureRoot = await mkdtemp(join(tmpdir(), "lucide-icon-glob-"));
901
+ await writeFile(
902
+ join(fixtureRoot, "package.json"),
903
+ JSON.stringify({
904
+ name: "lucide-icon-glob-fixture",
905
+ private: true,
906
+ version: "0.0.0",
907
+ type: "module",
908
+ }),
909
+ );
910
+ await writeFile(
911
+ join(fixtureRoot, "index.html"),
912
+ `<!doctype html><html><head></head><body><div id="root"></div>` +
913
+ `<script type="module" src="/src/main.tsx"></script></body></html>`,
914
+ );
915
+ const srcDir = join(fixtureRoot, "src");
916
+ await mkdir(srcDir, { recursive: true });
917
+ // Source imports NO icons: the glob include alone must pre-bundle them,
918
+ // exactly like a seeded template whose source hasn't used an icon yet.
919
+ await writeFile(join(srcDir, "main.tsx"), `export const root = "app";\n`);
920
+
921
+ // Minimal lucide-react mirroring the real layout: a package entry plus
922
+ // per-icon modules under dist/esm/icons/ (the paths the rewrite plugin
923
+ // emits).
924
+ const lucideDir = join(fixtureRoot, "node_modules", "lucide-react");
925
+ const iconsDir = join(lucideDir, "dist", "esm", "icons");
926
+ await mkdir(iconsDir, { recursive: true });
927
+ await writeFile(
928
+ join(lucideDir, "package.json"),
929
+ JSON.stringify({
930
+ name: "lucide-react",
931
+ version: "1.0.0",
932
+ type: "module",
933
+ main: "dist/esm/index.js",
934
+ module: "dist/esm/index.js",
935
+ }),
936
+ );
937
+ await writeFile(
938
+ join(lucideDir, "dist", "esm", "index.js"),
939
+ `export const icons = {};\n`,
940
+ );
941
+ for (const icon of ["check", "chevron-down"]) {
942
+ await writeFile(
943
+ join(iconsDir, `${icon}.js`),
944
+ `export default function ${icon.replace("-", "_")}() { return null; }\n`,
945
+ );
946
+ }
947
+ });
948
+
949
+ afterAll(async () => {
950
+ if (fixtureRoot) {
951
+ await rm(fixtureRoot, { recursive: true, force: true });
952
+ }
953
+ });
954
+
955
+ it("pre-bundles every icon deep path via the include glob", async () => {
956
+ const config = await resolveConfig(
957
+ {
958
+ root: fixtureRoot,
959
+ configFile: false,
960
+ logLevel: "silent",
961
+ optimizeDeps: OPTIMIZE_DEPS_CONFIG,
962
+ },
963
+ "serve",
964
+ );
965
+ const metadata = await optimizeDeps(config, true, false);
966
+ const optimized = Object.keys(metadata.optimized);
967
+
968
+ // The load-bearing serve-path contract: the id the rewrite plugin emits
969
+ // for an icon import must be EXACTLY an optimized-dep key, because
970
+ // `tryOptimizedResolve` matches by exact id. Asserting membership with the
971
+ // plugin's own `lucideIconModuleId` (instead of a hand-written string)
972
+ // means a drift on either side — plugin output shape or glob-registered
973
+ // ids — turns this red.
974
+ for (const importName of ["Check", "ChevronDown"]) {
975
+ expect(
976
+ optimized,
977
+ `"${lucideIconModuleId(importName)}" (the id the rewrite plugin emits ` +
978
+ `for "${importName}") is not an optimized-dep key (optimized: ` +
979
+ `[${optimized.join(", ")}]). Vite matches optimized deps by exact ` +
980
+ `id, so this icon's first import would be re-registered as a newly ` +
981
+ `discovered dependency and re-run the optimizer — the reload this ` +
982
+ `config exists to prevent.`,
983
+ ).toContain(lucideIconModuleId(importName));
984
+ }
985
+ }, 30_000);
986
+
987
+ it("rewrites named lucide imports to the .js-suffixed ids the optimizer registers", () => {
988
+ const { transformedCode, hasChanges } = transformLucideImports(
989
+ `import { Check, ChevronDown } from "lucide-react";\n`,
990
+ );
991
+
992
+ expect(hasChanges).toBe(true);
993
+ expect(transformedCode).toContain(
994
+ `import Check from "${lucideIconModuleId("Check")}"`,
995
+ );
996
+ expect(transformedCode).toContain(
997
+ `import ChevronDown from "${lucideIconModuleId("ChevronDown")}"`,
998
+ );
999
+ // Extensionless emissions would bypass the glob-registered `.js` ids and
1000
+ // re-run the optimizer on first use of a new icon.
1001
+ expect(transformedCode).not.toContain(`/icons/check"`);
1002
+ });
1003
+ });