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

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.
@@ -2119,9 +2119,13 @@ function startServe(options, internal = {}) {
2119
2119
  if (isBuild) {
2120
2120
  lines.push(`import manifest from ${JSON.stringify(MANIFEST_ID)};`);
2121
2121
  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;`,
2122
- // The plugin's manifest module normalizes lazy facade chunks
2123
- // (isDynamicEntry) so exactly one real entry remains flagged.
2124
- ` for (const key in manifest) {`, ` const chunk = manifest[key];`, ` if (chunk && chunk.isEntry && chunk.file) {`, ` clientEntryUrl = joinAssetPath(manifest._base, chunk.file);`, ` break;`, ` }`, ` }`, ` return clientEntryUrl;`, `}`);
2122
+ // The plugin's manifest module names the client entry it injected
2123
+ // into the build (`_entry`): every configured input is a genuine
2124
+ // `isEntry` record (a filesystem router's `buildInputs` lists every
2125
+ // route module), so scanning for the first flagged record would pick
2126
+ // whichever sorts first (#353). The scan stays as the fallback for
2127
+ // hand-rolled manifests without the stamp.
2128
+ ` 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;`, `}`);
2125
2129
  } else {
2126
2130
  const devHead = `<script>${devStylePatch}</script>` + `<script type="module" src="${joinBase(base, '/@vite/client')}"></script>`;
2127
2131
  lines.push(``, `const DEV_HEAD = ${JSON.stringify(devHead)};`);
@@ -2345,6 +2349,7 @@ function startServe(options, internal = {}) {
2345
2349
  }
2346
2350
  const build = env.command === 'build';
2347
2351
  const clientInput = entries.generated ? ENTRY_CLIENT_ID : path.resolve(root, entries.entryClient);
2352
+ internal.onClientEntryResolved?.(clientInput);
2348
2353
  // Real files only — the dep scanner can't crawl virtual modules.
2349
2354
  // (In client mode the resolved document joins the scan/style roots
2350
2355
  // even with an authored client entry; in SSR mode authored entries
@@ -3450,6 +3455,23 @@ function getExtension(filename) {
3450
3455
  const index = filename.lastIndexOf('.');
3451
3456
  return index < 0 ? '' : filename.substring(index).replace(/\?.+$/, '');
3452
3457
  }
3458
+ // The packages whose dev/production server builds are selected by the
3459
+ // `development` export condition. A dependency on either means the package
3460
+ // consumes the runtime and must resolve it through Vite in dev.
3461
+ const SOLID_RUNTIME_PKGS = ['solid-js', '@solidjs/web'];
3462
+
3463
+ // Tooling that declares solid-js as a peer but never runs inside the SSR
3464
+ // module runner. Kept out of the crawl entirely: classifying them as
3465
+ // semi-framework would also crawl THEIR dependencies, which vitefu deep-
3466
+ // includes in the client optimizer (`@solidjs/vite-plugin > @babel/core`
3467
+ // pre-bundled for the browser — ~2.6 MB of dead weight per cold start).
3468
+ // Mirrors vite-plugin-svelte's isCommonDepWithoutSvelteField list.
3469
+ const NON_RUNTIME_SOLID_PKGS = ['@solidjs/vite-plugin', 'vite', 'vitest', 'eslint-plugin-solid'];
3470
+ const NON_RUNTIME_SOLID_PREFIXES = ['vite-plugin-', 'eslint-plugin-', 'prettier-plugin-', '@types/'];
3471
+ function isNonRuntimeSolidPkg(name) {
3472
+ const bare = name.slice(name.lastIndexOf('/') + 1);
3473
+ return NON_RUNTIME_SOLID_PKGS.includes(name) || NON_RUNTIME_SOLID_PREFIXES.some(p => (p.startsWith('@') ? name : bare).startsWith(p));
3474
+ }
3453
3475
  function containsSolidField(fields) {
3454
3476
  const keys = Object.keys(fields);
3455
3477
  for (let i = 0; i < keys.length; i++) {
@@ -3676,6 +3698,66 @@ function normalizeEmittedLazyEntries(manifest, {
3676
3698
  }
3677
3699
  }
3678
3700
  }
3701
+
3702
+ /**
3703
+ * The manifest key of THE client entry — the chunk whose `<script
3704
+ * type="module">` boots the page and whose static import graph carries the
3705
+ * global CSS. `isEntry` cannot answer this: every configured build input is
3706
+ * a genuine entry (#347 keeps them flagged), and plugins routinely add more
3707
+ * inputs than the application entry (filesystem-routing's `buildInputs`
3708
+ * lists every route module, and route keys sort ahead of the plugin's own
3709
+ * `virtual:` entry). So the identity comes from configuration instead: the
3710
+ * entry start mode injected itself, or — outside start mode — the single
3711
+ * configured input when there is exactly one (including Vite's default
3712
+ * `index.html`). Several inputs and no start entry: no answer (null), and
3713
+ * consumers keep their first-`isEntry` scan.
3714
+ *
3715
+ * Matched by key or `src`, the same two spellings `isConfiguredEntry` uses.
3716
+ */
3717
+ function resolveClientEntryKey(manifest, startClientEntryId, clientBuild, root) {
3718
+ let entryId = startClientEntryId;
3719
+ if (!entryId) {
3720
+ const input = configuredBuildInput(clientBuild);
3721
+ const raw = input == null ? ['index.html'] : typeof input === 'string' ? [input] : Array.isArray(input) ? input : Object.values(input);
3722
+ if (raw.length !== 1 || typeof raw[0] !== 'string') return null;
3723
+ entryId = raw[0];
3724
+ }
3725
+ const {
3726
+ manifestKeys
3727
+ } = resolveConfiguredEntries(entryId, root);
3728
+ for (const key in manifest) {
3729
+ const record = manifest[key];
3730
+ if (!record || typeof record !== 'object' || !record.file) continue;
3731
+ if (manifestKeys.has(key) || typeof record.src === 'string' && manifestKeys.has(record.src)) {
3732
+ return key;
3733
+ }
3734
+ }
3735
+ return null;
3736
+ }
3737
+
3738
+ /**
3739
+ * Serializes the plugin's manifest module with the client entry made
3740
+ * explicit: `_entry` names its key (the generated handler reads it before
3741
+ * falling back to scanning for `isEntry`), and its record is moved to the
3742
+ * front. The ordering matters for consumers that still identify the entry
3743
+ * by the first `isEntry` record — `@solidjs/web`'s `registerEntryAssets`,
3744
+ * which links the entry graph's stylesheets and modulepreloads into
3745
+ * `<head>`, and hand-rolled server entries — so they and `_entry` agree on
3746
+ * the same chunk. Other configured inputs keep `isEntry`; they are genuine
3747
+ * entries, just not the one the document boots.
3748
+ */
3749
+ function stampClientEntry(manifest, entryKey, base) {
3750
+ const ordered = {};
3751
+ if (entryKey && manifest[entryKey]) {
3752
+ ordered[entryKey] = manifest[entryKey];
3753
+ }
3754
+ for (const key in manifest) {
3755
+ if (key !== entryKey) ordered[key] = manifest[key];
3756
+ }
3757
+ ordered._base = base;
3758
+ if (entryKey && manifest[entryKey]) ordered._entry = entryKey;
3759
+ return ordered;
3760
+ }
3679
3761
  function solidPlugin(options = {}) {
3680
3762
  if (typeof options.ssr === 'object') {
3681
3763
  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 })`.');
@@ -3739,6 +3821,11 @@ function solidPlugin(options = {}) {
3739
3821
  // two-invocation build (`vite build --ssr`) still knows the client's
3740
3822
  // entries when it bakes the client manifest in.
3741
3823
  let clientBuildConfig = null;
3824
+ // The client entry start mode injects into the client build's input
3825
+ // (reported by startServe): the one input that IS the application entry,
3826
+ // as opposed to further inputs other plugins add (e.g. filesystem-routing's
3827
+ // `buildInputs`, which lists every route module). Null outside start mode.
3828
+ let startClientEntryId = null;
3742
3829
  let solidPkgsConfig;
3743
3830
  const tsrxCss = new Map();
3744
3831
 
@@ -3900,6 +3987,28 @@ function solidPlugin(options = {}) {
3900
3987
  isBuild: command === 'build',
3901
3988
  isFrameworkPkgByJson(pkgJson) {
3902
3989
  return containsSolidField(pkgJson.exports || {});
3990
+ },
3991
+ // `false` = neither framework nor semi-framework, and don't crawl
3992
+ // its deps; `undefined` = unknown, fall through to the json checks.
3993
+ isFrameworkPkgByName(name) {
3994
+ return isNonRuntimeSolidPkg(name) ? false : undefined;
3995
+ },
3996
+ // Under `vite dev` the runtime must not be split in two. Inlined
3997
+ // modules resolve `solid-js` through Vite with `development` (its dev
3998
+ // server build); an externalized package's own imports are resolved by
3999
+ // Node, which has no `development` condition, so it loads the
4000
+ // production build instead. Both then run, each with its own
4001
+ // `sharedConfig` — the manifest `renderToStream` sets lands on one and
4002
+ // `lazy()` reads the other. `resolve.externalConditions` below only
4003
+ // fixes the external's own entry, not what it imports, so every
4004
+ // package that consumes the runtime has to go through Vite as well.
4005
+ // Semi-framework is the right class: `ssr.noExternal` without
4006
+ // `optimizeDeps.exclude`, since these hold no raw Solid components.
4007
+ isSemiFrameworkPkgByJson(pkgJson) {
4008
+ // Same gate as the core inlining in configEnvironment: dev serve
4009
+ // only, never vitest (it manages inlining via test.server.deps).
4010
+ if (!replaceDev || isTestMode) return false;
4011
+ return SOLID_RUNTIME_PKGS.some(name => pkgJson.dependencies?.[name] || pkgJson.peerDependencies?.[name]);
3903
4012
  }
3904
4013
  });
3905
4014
 
@@ -4073,8 +4182,16 @@ function solidPlugin(options = {}) {
4073
4182
  // Only set resolve.external if noExternal is not true (to avoid conflicts with plugins like Cloudflare)
4074
4183
  if (name === 'ssr' && solidPkgsConfig) {
4075
4184
  if (config.resolve.noExternal !== true) {
4076
- config.resolve.noExternal = [...(Array.isArray(config.resolve.noExternal) ? config.resolve.noExternal : []), ...solidPkgsConfig.ssr.noExternal];
4077
- config.resolve.external = [...(Array.isArray(config.resolve.external) ? config.resolve.external : []), ...solidPkgsConfig.ssr.external];
4185
+ const noExternal = [...(Array.isArray(config.resolve.noExternal) ? config.resolve.noExternal : []), ...solidPkgsConfig.ssr.noExternal];
4186
+ config.resolve.noExternal = noExternal;
4187
+ // vitefu externalizes the non-framework deps of every framework
4188
+ // package in dev, and Vite gives `external` precedence over
4189
+ // `noExternal`. A framework package that lists solid-js or
4190
+ // @solidjs/web under `dependencies` (not peer — e.g.
4191
+ // @tanstack/solid-router 2.0.0-rc.7 → @solidjs/web) would therefore
4192
+ // re-externalize a core inlined above and split the runtime again.
4193
+ // Nothing inlined may appear in `external`.
4194
+ config.resolve.external = [...(Array.isArray(config.resolve.external) ? config.resolve.external : []), ...solidPkgsConfig.ssr.external.filter(dep => !noExternal.includes(dep))];
4078
4195
  }
4079
4196
  }
4080
4197
  },
@@ -4234,8 +4351,7 @@ function solidPlugin(options = {}) {
4234
4351
  warn: message => this.warn(message),
4235
4352
  repairDynamicEntries: true
4236
4353
  });
4237
- manifest._base = base;
4238
- return `export default ${JSON.stringify(manifest)};`;
4354
+ return `export default ${JSON.stringify(stampClientEntry(manifest, resolveClientEntryKey(manifest, startClientEntryId, clientBuildConfig, projectRoot), base))};`;
4239
4355
  }
4240
4356
  // SSR build before the client build produced a manifest: bake in the
4241
4357
  // dev-shaped fallback (registry miss degrades to js-only resolution).
@@ -4547,6 +4663,9 @@ function solidPlugin(options = {}) {
4547
4663
  onDocumentResolved(documentPath) {
4548
4664
  // Normalize to forward slashes to match Vite's transform ids.
4549
4665
  documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
4666
+ },
4667
+ onClientEntryResolved(entryId) {
4668
+ startClientEntryId = entryId;
4550
4669
  }
4551
4670
  }));
4552
4671
  }