@solidjs/vite-plugin 3.0.0-next.40 → 3.0.0-next.41

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.
@@ -2095,9 +2095,13 @@ function startServe(options, internal = {}) {
2095
2095
  if (isBuild) {
2096
2096
  lines.push(`import manifest from ${JSON.stringify(MANIFEST_ID)};`);
2097
2097
  lines.push(``, `function joinAssetPath(base, file) {`, ` if (typeof base !== 'string' || !base) base = '/';`, ` if (base[base.length - 1] !== '/') base += '/';`, ` return base + (file[0] === '/' ? file.slice(1) : file);`, `}`, ``, `let clientEntryUrl;`, `function resolveClientEntry() {`, ` if (clientEntryUrl !== undefined) return clientEntryUrl;`, ` clientEntryUrl = null;`,
2098
- // The plugin's manifest module normalizes lazy facade chunks
2099
- // (isDynamicEntry) so exactly one real entry remains flagged.
2100
- ` for (const key in manifest) {`, ` const chunk = manifest[key];`, ` if (chunk && chunk.isEntry && chunk.file) {`, ` clientEntryUrl = joinAssetPath(manifest._base, chunk.file);`, ` break;`, ` }`, ` }`, ` return clientEntryUrl;`, `}`);
2098
+ // The plugin's manifest module names the client entry it injected
2099
+ // into the build (`_entry`): every configured input is a genuine
2100
+ // `isEntry` record (a filesystem router's `buildInputs` lists every
2101
+ // route module), so scanning for the first flagged record would pick
2102
+ // whichever sorts first (#353). The scan stays as the fallback for
2103
+ // hand-rolled manifests without the stamp.
2104
+ ` const stamped = manifest._entry && manifest[manifest._entry];`, ` if (stamped && stamped.file) {`, ` return (clientEntryUrl = joinAssetPath(manifest._base, stamped.file));`, ` }`, ` for (const key in manifest) {`, ` const chunk = manifest[key];`, ` if (chunk && chunk.isEntry && chunk.file) {`, ` clientEntryUrl = joinAssetPath(manifest._base, chunk.file);`, ` break;`, ` }`, ` }`, ` return clientEntryUrl;`, `}`);
2101
2105
  } else {
2102
2106
  const devHead = `<script>${devStylePatch}</script>` + `<script type="module" src="${joinBase(base, '/@vite/client')}"></script>`;
2103
2107
  lines.push(``, `const DEV_HEAD = ${JSON.stringify(devHead)};`);
@@ -2321,6 +2325,7 @@ function startServe(options, internal = {}) {
2321
2325
  }
2322
2326
  const build = env.command === 'build';
2323
2327
  const clientInput = entries.generated ? ENTRY_CLIENT_ID : path.resolve(root, entries.entryClient);
2328
+ internal.onClientEntryResolved?.(clientInput);
2324
2329
  // Real files only — the dep scanner can't crawl virtual modules.
2325
2330
  // (In client mode the resolved document joins the scan/style roots
2326
2331
  // even with an authored client entry; in SSR mode authored entries
@@ -3426,6 +3431,23 @@ function getExtension(filename) {
3426
3431
  const index = filename.lastIndexOf('.');
3427
3432
  return index < 0 ? '' : filename.substring(index).replace(/\?.+$/, '');
3428
3433
  }
3434
+ // The packages whose dev/production server builds are selected by the
3435
+ // `development` export condition. A dependency on either means the package
3436
+ // consumes the runtime and must resolve it through Vite in dev.
3437
+ const SOLID_RUNTIME_PKGS = ['solid-js', '@solidjs/web'];
3438
+
3439
+ // Tooling that declares solid-js as a peer but never runs inside the SSR
3440
+ // module runner. Kept out of the crawl entirely: classifying them as
3441
+ // semi-framework would also crawl THEIR dependencies, which vitefu deep-
3442
+ // includes in the client optimizer (`@solidjs/vite-plugin > @babel/core`
3443
+ // pre-bundled for the browser — ~2.6 MB of dead weight per cold start).
3444
+ // Mirrors vite-plugin-svelte's isCommonDepWithoutSvelteField list.
3445
+ const NON_RUNTIME_SOLID_PKGS = ['@solidjs/vite-plugin', 'vite', 'vitest', 'eslint-plugin-solid'];
3446
+ const NON_RUNTIME_SOLID_PREFIXES = ['vite-plugin-', 'eslint-plugin-', 'prettier-plugin-', '@types/'];
3447
+ function isNonRuntimeSolidPkg(name) {
3448
+ const bare = name.slice(name.lastIndexOf('/') + 1);
3449
+ return NON_RUNTIME_SOLID_PKGS.includes(name) || NON_RUNTIME_SOLID_PREFIXES.some(p => (p.startsWith('@') ? name : bare).startsWith(p));
3450
+ }
3429
3451
  function containsSolidField(fields) {
3430
3452
  const keys = Object.keys(fields);
3431
3453
  for (let i = 0; i < keys.length; i++) {
@@ -3652,6 +3674,66 @@ function normalizeEmittedLazyEntries(manifest, {
3652
3674
  }
3653
3675
  }
3654
3676
  }
3677
+
3678
+ /**
3679
+ * The manifest key of THE client entry — the chunk whose `<script
3680
+ * type="module">` boots the page and whose static import graph carries the
3681
+ * global CSS. `isEntry` cannot answer this: every configured build input is
3682
+ * a genuine entry (#347 keeps them flagged), and plugins routinely add more
3683
+ * inputs than the application entry (filesystem-routing's `buildInputs`
3684
+ * lists every route module, and route keys sort ahead of the plugin's own
3685
+ * `virtual:` entry). So the identity comes from configuration instead: the
3686
+ * entry start mode injected itself, or — outside start mode — the single
3687
+ * configured input when there is exactly one (including Vite's default
3688
+ * `index.html`). Several inputs and no start entry: no answer (null), and
3689
+ * consumers keep their first-`isEntry` scan.
3690
+ *
3691
+ * Matched by key or `src`, the same two spellings `isConfiguredEntry` uses.
3692
+ */
3693
+ function resolveClientEntryKey(manifest, startClientEntryId, clientBuild, root) {
3694
+ let entryId = startClientEntryId;
3695
+ if (!entryId) {
3696
+ const input = configuredBuildInput(clientBuild);
3697
+ const raw = input == null ? ['index.html'] : typeof input === 'string' ? [input] : Array.isArray(input) ? input : Object.values(input);
3698
+ if (raw.length !== 1 || typeof raw[0] !== 'string') return null;
3699
+ entryId = raw[0];
3700
+ }
3701
+ const {
3702
+ manifestKeys
3703
+ } = resolveConfiguredEntries(entryId, root);
3704
+ for (const key in manifest) {
3705
+ const record = manifest[key];
3706
+ if (!record || typeof record !== 'object' || !record.file) continue;
3707
+ if (manifestKeys.has(key) || typeof record.src === 'string' && manifestKeys.has(record.src)) {
3708
+ return key;
3709
+ }
3710
+ }
3711
+ return null;
3712
+ }
3713
+
3714
+ /**
3715
+ * Serializes the plugin's manifest module with the client entry made
3716
+ * explicit: `_entry` names its key (the generated handler reads it before
3717
+ * falling back to scanning for `isEntry`), and its record is moved to the
3718
+ * front. The ordering matters for consumers that still identify the entry
3719
+ * by the first `isEntry` record — `@solidjs/web`'s `registerEntryAssets`,
3720
+ * which links the entry graph's stylesheets and modulepreloads into
3721
+ * `<head>`, and hand-rolled server entries — so they and `_entry` agree on
3722
+ * the same chunk. Other configured inputs keep `isEntry`; they are genuine
3723
+ * entries, just not the one the document boots.
3724
+ */
3725
+ function stampClientEntry(manifest, entryKey, base) {
3726
+ const ordered = {};
3727
+ if (entryKey && manifest[entryKey]) {
3728
+ ordered[entryKey] = manifest[entryKey];
3729
+ }
3730
+ for (const key in manifest) {
3731
+ if (key !== entryKey) ordered[key] = manifest[key];
3732
+ }
3733
+ ordered._base = base;
3734
+ if (entryKey && manifest[entryKey]) ordered._entry = entryKey;
3735
+ return ordered;
3736
+ }
3655
3737
  function solidPlugin(options = {}) {
3656
3738
  if (typeof options.ssr === 'object') {
3657
3739
  throw new Error('[@solidjs/vite-plugin] `ssr` now only accepts a boolean ("is the app server-rendered"); ' + 'move start-mode options to `start: {}` and set `ssr: true`. Example: ' + '`solid({ ssr: { document: … } })` becomes `solid({ start: { document: … }, ssr: true })`.');
@@ -3715,6 +3797,11 @@ function solidPlugin(options = {}) {
3715
3797
  // two-invocation build (`vite build --ssr`) still knows the client's
3716
3798
  // entries when it bakes the client manifest in.
3717
3799
  let clientBuildConfig = null;
3800
+ // The client entry start mode injects into the client build's input
3801
+ // (reported by startServe): the one input that IS the application entry,
3802
+ // as opposed to further inputs other plugins add (e.g. filesystem-routing's
3803
+ // `buildInputs`, which lists every route module). Null outside start mode.
3804
+ let startClientEntryId = null;
3718
3805
  let solidPkgsConfig;
3719
3806
  const tsrxCss = new Map();
3720
3807
 
@@ -3876,6 +3963,28 @@ function solidPlugin(options = {}) {
3876
3963
  isBuild: command === 'build',
3877
3964
  isFrameworkPkgByJson(pkgJson) {
3878
3965
  return containsSolidField(pkgJson.exports || {});
3966
+ },
3967
+ // `false` = neither framework nor semi-framework, and don't crawl
3968
+ // its deps; `undefined` = unknown, fall through to the json checks.
3969
+ isFrameworkPkgByName(name) {
3970
+ return isNonRuntimeSolidPkg(name) ? false : undefined;
3971
+ },
3972
+ // Under `vite dev` the runtime must not be split in two. Inlined
3973
+ // modules resolve `solid-js` through Vite with `development` (its dev
3974
+ // server build); an externalized package's own imports are resolved by
3975
+ // Node, which has no `development` condition, so it loads the
3976
+ // production build instead. Both then run, each with its own
3977
+ // `sharedConfig` — the manifest `renderToStream` sets lands on one and
3978
+ // `lazy()` reads the other. `resolve.externalConditions` below only
3979
+ // fixes the external's own entry, not what it imports, so every
3980
+ // package that consumes the runtime has to go through Vite as well.
3981
+ // Semi-framework is the right class: `ssr.noExternal` without
3982
+ // `optimizeDeps.exclude`, since these hold no raw Solid components.
3983
+ isSemiFrameworkPkgByJson(pkgJson) {
3984
+ // Same gate as the core inlining in configEnvironment: dev serve
3985
+ // only, never vitest (it manages inlining via test.server.deps).
3986
+ if (!replaceDev || isTestMode) return false;
3987
+ return SOLID_RUNTIME_PKGS.some(name => pkgJson.dependencies?.[name] || pkgJson.peerDependencies?.[name]);
3879
3988
  }
3880
3989
  });
3881
3990
 
@@ -4049,8 +4158,16 @@ function solidPlugin(options = {}) {
4049
4158
  // Only set resolve.external if noExternal is not true (to avoid conflicts with plugins like Cloudflare)
4050
4159
  if (name === 'ssr' && solidPkgsConfig) {
4051
4160
  if (config.resolve.noExternal !== true) {
4052
- config.resolve.noExternal = [...(Array.isArray(config.resolve.noExternal) ? config.resolve.noExternal : []), ...solidPkgsConfig.ssr.noExternal];
4053
- config.resolve.external = [...(Array.isArray(config.resolve.external) ? config.resolve.external : []), ...solidPkgsConfig.ssr.external];
4161
+ const noExternal = [...(Array.isArray(config.resolve.noExternal) ? config.resolve.noExternal : []), ...solidPkgsConfig.ssr.noExternal];
4162
+ config.resolve.noExternal = noExternal;
4163
+ // vitefu externalizes the non-framework deps of every framework
4164
+ // package in dev, and Vite gives `external` precedence over
4165
+ // `noExternal`. A framework package that lists solid-js or
4166
+ // @solidjs/web under `dependencies` (not peer — e.g.
4167
+ // @tanstack/solid-router 2.0.0-rc.7 → @solidjs/web) would therefore
4168
+ // re-externalize a core inlined above and split the runtime again.
4169
+ // Nothing inlined may appear in `external`.
4170
+ config.resolve.external = [...(Array.isArray(config.resolve.external) ? config.resolve.external : []), ...solidPkgsConfig.ssr.external.filter(dep => !noExternal.includes(dep))];
4054
4171
  }
4055
4172
  }
4056
4173
  },
@@ -4210,8 +4327,7 @@ function solidPlugin(options = {}) {
4210
4327
  warn: message => this.warn(message),
4211
4328
  repairDynamicEntries: true
4212
4329
  });
4213
- manifest._base = base;
4214
- return `export default ${JSON.stringify(manifest)};`;
4330
+ return `export default ${JSON.stringify(stampClientEntry(manifest, resolveClientEntryKey(manifest, startClientEntryId, clientBuildConfig, projectRoot), base))};`;
4215
4331
  }
4216
4332
  // SSR build before the client build produced a manifest: bake in the
4217
4333
  // dev-shaped fallback (registry miss degrades to js-only resolution).
@@ -4523,6 +4639,9 @@ function solidPlugin(options = {}) {
4523
4639
  onDocumentResolved(documentPath) {
4524
4640
  // Normalize to forward slashes to match Vite's transform ids.
4525
4641
  documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
4642
+ },
4643
+ onClientEntryResolved(entryId) {
4644
+ startClientEntryId = entryId;
4526
4645
  }
4527
4646
  }));
4528
4647
  }