@superblocksteam/sdk 2.0.141 → 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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/dev-utils/dev-server.optimize-deps.test.mjs +242 -2
- package/dist/dev-utils/dev-server.optimize-deps.test.mjs.map +1 -1
- package/dist/dev-utils/optimize-deps-config.d.mts +14 -0
- package/dist/dev-utils/optimize-deps-config.d.mts.map +1 -1
- package/dist/dev-utils/optimize-deps-config.mjs +104 -8
- package/dist/dev-utils/optimize-deps-config.mjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/telemetry/startup-context.d.ts +17 -0
- package/dist/telemetry/startup-context.d.ts.map +1 -0
- package/dist/telemetry/startup-context.js +43 -0
- package/dist/telemetry/startup-context.js.map +1 -0
- package/dist/telemetry/startup-context.test.d.ts +2 -0
- package/dist/telemetry/startup-context.test.d.ts.map +1 -0
- package/dist/telemetry/startup-context.test.js +70 -0
- package/dist/telemetry/startup-context.test.js.map +1 -0
- package/dist/vite-plugin-optimize-lucide-imports.d.ts +20 -0
- package/dist/vite-plugin-optimize-lucide-imports.d.ts.map +1 -1
- package/dist/vite-plugin-optimize-lucide-imports.js +19 -4
- package/dist/vite-plugin-optimize-lucide-imports.js.map +1 -1
- package/package.json +6 -6
- package/src/dev-utils/dev-server.optimize-deps.test.mts +334 -2
- package/src/dev-utils/optimize-deps-config.mts +106 -8
- package/src/index.ts +7 -0
- package/src/telemetry/startup-context.test.ts +102 -0
- package/src/telemetry/startup-context.ts +77 -0
- package/src/vite-plugin-optimize-lucide-imports.ts +21 -4
- 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;
|
|
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 "
|
|
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":"AAEA,2CAA2C;AAC3C,MAAM,qBAAqB,GACzB,mEAAmE,CAAC;AAEtE;;;GAGG;AACH,SAAS,gCAAgC;IACvC,OAAO;QACL,IAAI,EAAE,+BAA+B;QACrC,SAAS,CACP,UAAkB,EAClB,QAAgB;YAEhB,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,QAAQ,CAAC;gBAAE,OAAO,IAAI,CAAC;YAErD,IAAI,CAAC;gBACH,wDAAwD;gBACxD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAc,CAAC;oBAAE,OAAO,IAAI,CAAC;gBAEtD,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,GACnC,sBAAsB,CAAC,UAAU,CAAC,CAAC;gBAErC,IAAI,UAAU,EAAE,CAAC;oBACf,OAAO;wBACL,IAAI,EAAE,eAAe;wBACrB,GAAG,EAAE,IAAI,EAAE,wCAAwC;qBACpD,CAAC;gBACJ,CAAC;gBAED,OAAO,IAAI,CAAC;YACd,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,oBAAoB,CAAC,KAAK,CAAC,CAAC;gBAC5B,OAAO,IAAI,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,IAAY,EAAE,EAAU;IAC5C,OAAO,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;AAC7B,CAAC;AAED
|
|
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.
|
|
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.
|
|
61
|
-
"@superblocksteam/
|
|
62
|
-
"@superblocksteam/
|
|
63
|
-
"@superblocksteam/
|
|
64
|
-
"@superblocksteam/
|
|
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 {
|
|
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
|
+
});
|