@sveltejs/kit 3.0.0-next.19 → 3.0.0-next.20

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 (44) hide show
  1. package/package.json +12 -12
  2. package/src/core/adapt/builder.js +2 -1
  3. package/src/core/adapt/index.js +1 -1
  4. package/src/core/env.js +1 -1
  5. package/src/core/postbuild/prerender.js +61 -8
  6. package/src/core/sync/write_env.js +1 -1
  7. package/src/core/sync/write_server.js +2 -2
  8. package/src/core/sync/write_tsconfig/index.js +1 -3
  9. package/src/core/sync/write_tsconfig/utils.js +0 -24
  10. package/src/exports/env/index.js +1 -1
  11. package/src/exports/env/public.d.ts +55 -0
  12. package/src/exports/hooks/public.d.ts +194 -0
  13. package/src/exports/hooks/sequence.js +4 -3
  14. package/src/exports/index.js +1 -1
  15. package/src/exports/internal/env.js +1 -1
  16. package/src/exports/params/public.d.ts +2 -2
  17. package/src/exports/public.d.ts +1 -757
  18. package/src/exports/vite/dev/index.js +2 -2
  19. package/src/exports/vite/index.js +541 -384
  20. package/src/exports/vite/utils.js +0 -16
  21. package/src/runtime/app/internal/transport.js +1 -1
  22. package/src/runtime/app/server/public.d.ts +519 -0
  23. package/src/runtime/app/server/remote/command.js +1 -1
  24. package/src/runtime/app/server/remote/form.js +1 -1
  25. package/src/runtime/app/server/remote/prerender.js +1 -1
  26. package/src/runtime/app/server/remote/query.js +2 -1
  27. package/src/runtime/app/server/remote/requested.js +1 -1
  28. package/src/runtime/client/client.js +1 -1
  29. package/src/runtime/client/remote-functions/command.svelte.js +1 -1
  30. package/src/runtime/client/remote-functions/form.svelte.js +1 -1
  31. package/src/runtime/client/remote-functions/prerender.svelte.js +1 -1
  32. package/src/runtime/client/remote-functions/query/index.js +1 -1
  33. package/src/runtime/client/remote-functions/query-batch.svelte.js +1 -1
  34. package/src/runtime/client/remote-functions/query-live/index.js +1 -1
  35. package/src/runtime/client/remote-functions/shared.svelte.js +1 -1
  36. package/src/runtime/server/errors.js +2 -2
  37. package/src/runtime/server/internal.js +39 -0
  38. package/src/runtime/server/page/load_data.js +1 -1
  39. package/src/runtime/server/remote-functions.js +17 -5
  40. package/src/runtime/server/respond.js +1 -1
  41. package/src/types/internal.d.ts +14 -13
  42. package/src/version.js +1 -1
  43. package/types/index.d.ts +1403 -1403
  44. package/types/index.d.ts.map +68 -62
@@ -1,4 +1,5 @@
1
- /** @import { EnvVarConfig, KitConfig } from '@sveltejs/kit' */
1
+ /** @import { KitConfig } from '@sveltejs/kit' */
2
+ /** @import { EnvVarConfig } from '@sveltejs/kit/env' */
2
3
  /** @import { Options, SvelteConfig } from '@sveltejs/vite-plugin-svelte' */
3
4
  /** @import { PreprocessorGroup } from 'svelte/compiler' */
4
5
  /** @import { Asset, BuildData, ManifestData, Prerendered, RemoteChunk, RemoteInternals, RouteData, ServerMetadata, ValidatedConfig, ValidatedKitConfig } from 'types' */
@@ -41,6 +42,7 @@ import {
41
42
  server_only_module_pattern
42
43
  } from './utils.js';
43
44
  import { stackless } from '../../utils/error.js';
45
+ import { adapt } from '../../core/adapt/index.js';
44
46
  import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
45
47
  import prerender from '../../core/postbuild/prerender.js';
46
48
  import analyse from '../../core/postbuild/analyse.js';
@@ -274,8 +276,8 @@ function kit({ svelte_config }) {
274
276
  /** @type {ManifestData} */
275
277
  let manifest_data;
276
278
 
277
- /** @type {ServerMetadata | undefined} only set at build time once analysis is finished */
278
- let build_metadata = undefined;
279
+ /** @type {ServerMetadata | null} set at build time once analysis has finished */
280
+ let build_metadata = null;
279
281
 
280
282
  /** @type {UserConfig} */
281
283
  let initial_config;
@@ -297,7 +299,7 @@ function kit({ svelte_config }) {
297
299
  * in which chunks, so that we can later determine which routes use which features
298
300
  * @type {Record<string, string[]>}
299
301
  */
300
- const tracked_features = {};
302
+ let tracked_features = {};
301
303
 
302
304
  const sourcemapIgnoreList = /** @param {string} relative_path */ (relative_path) =>
303
305
  relative_path.includes('node_modules') || relative_path.includes(kit.outDir);
@@ -885,6 +887,11 @@ function kit({ svelte_config }) {
885
887
  // No chain from this server-only module to a client entrypoint was found —
886
888
  // the module is only imported from server code, which is valid.
887
889
  }
890
+ },
891
+
892
+ // avoid watch mode rebuilds using stale import map data
893
+ buildEnd() {
894
+ import_map.clear();
888
895
  }
889
896
  };
890
897
 
@@ -892,7 +899,7 @@ function kit({ svelte_config }) {
892
899
  let dev_server;
893
900
 
894
901
  /** @type {RemoteChunk[]} */
895
- const remotes = [];
902
+ let remotes = [];
896
903
 
897
904
  /** @type {Map<string, string>} Maps remote hash -> original module id */
898
905
  const remote_original_by_hash = new Map();
@@ -1049,6 +1056,12 @@ function kit({ svelte_config }) {
1049
1056
  map: null
1050
1057
  };
1051
1058
  }
1059
+ },
1060
+
1061
+ buildEnd() {
1062
+ if (this.environment.config.consumer === 'server') {
1063
+ emitted_remote_hashes.clear();
1064
+ }
1052
1065
  }
1053
1066
  };
1054
1067
 
@@ -1079,10 +1092,10 @@ function kit({ svelte_config }) {
1079
1092
  /** @type {Prerendered} */
1080
1093
  let prerendered;
1081
1094
 
1082
- /** @type {Array<{ path: string }>} */
1083
- let immutable;
1084
- /** @type {string} */
1085
- let manifest_data_code;
1095
+ /** @type {Array<{ path: string }> | null} */
1096
+ let immutable = null;
1097
+ /** @type {string | null} */
1098
+ let manifest_data_code = null;
1086
1099
 
1087
1100
  /**
1088
1101
  * Creates the service worker virtual modules
@@ -1275,8 +1288,10 @@ function kit({ svelte_config }) {
1275
1288
  }
1276
1289
  };
1277
1290
 
1278
- /** @type {() => Promise<void> | undefined} */
1279
- let finalise;
1291
+ /** @type {Map<string, Rolldown.RolldownOutput['output']>} */
1292
+ const watch_build_output = new Map();
1293
+ /** @type {(() => Promise<void>) | null} */
1294
+ let finalise = null;
1280
1295
 
1281
1296
  /** @type {Plugin} */
1282
1297
  const plugin_compile = {
@@ -1417,6 +1432,12 @@ function kit({ svelte_config }) {
1417
1432
  }
1418
1433
 
1419
1434
  handler(warning);
1435
+ },
1436
+ watch: {
1437
+ exclude: [
1438
+ // Ignore all siblings of config.outDir/generated
1439
+ `${out_dir}/generated/**`
1440
+ ]
1420
1441
  }
1421
1442
  },
1422
1443
  emptyOutDir: false,
@@ -1594,7 +1615,16 @@ function kit({ svelte_config }) {
1594
1615
  }
1595
1616
  },
1596
1617
 
1597
- generateBundle() {
1618
+ generateBundle(_options, bundle) {
1619
+ // a watched build returns a watcher rather than the build output from
1620
+ // `builder.build` so we need to retrieve it from the generateBundle hook
1621
+ if (this.meta.watchMode) {
1622
+ watch_build_output.set(
1623
+ this.environment.name,
1624
+ /** @type {Rolldown.RolldownOutput['output']} */ (Object.values(bundle))
1625
+ );
1626
+ }
1627
+
1598
1628
  if (this.environment.config.consumer !== 'client') return;
1599
1629
 
1600
1630
  this.emitFile({
@@ -1605,449 +1635,540 @@ function kit({ svelte_config }) {
1605
1635
  },
1606
1636
 
1607
1637
  async buildApp(builder) {
1608
- // clears the output directories
1609
- if (!builder.config.build.watch) {
1610
- fs.rmSync(out, { force: true, recursive: true });
1611
- }
1638
+ fs.rmSync(out, { force: true, recursive: true });
1612
1639
  fs.mkdirSync(out, { recursive: true });
1613
1640
 
1614
- await load_and_validate_params({
1615
- routes: manifest_data.routes,
1616
- params_path: manifest_data.params,
1617
- root
1618
- });
1641
+ const verbose = builder.config.logLevel === 'info';
1642
+ const log = logger({ verbose });
1619
1643
 
1620
- const { output: server_chunks } = /** @type {Rolldown.RolldownOutput} */ (
1621
- await builder.build(builder.environments.ssr)
1622
- );
1644
+ let ssr_build = await builder.build(builder.environments.ssr);
1623
1645
 
1624
- // Replace manifest placeholders in SSR output. `assets` and `routes`
1625
- // are known from `manifest_data`. `immutable` and `prerendered` are not
1626
- // known yet they get sentinel strings that are replaced after
1627
- // the client build and after prerendering respectively.
1628
- replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
1629
- assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1630
- routes: get_manifest_routes(manifest_data.routes)
1631
- });
1646
+ /** @param {Rolldown.RolldownOutput['output']} server_chunks */
1647
+ const process_ssr_build = async (server_chunks) => {
1648
+ // Replace manifest placeholders in SSR output. `assets` and `routes`
1649
+ // are known from `manifest_data`. `immutable` and `prerendered` are not
1650
+ // known yet — they get sentinel strings that are replaced after
1651
+ // the client build and after prerendering respectively.
1652
+ replace_manifest_placeholder_variables(server_chunks, `${out}/server`, {
1653
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1654
+ routes: get_manifest_routes(manifest_data.routes)
1655
+ });
1632
1656
 
1633
- const verbose = builder.config.logLevel === 'info';
1634
- const log = logger({ verbose });
1657
+ vite_server_manifest = /** @type {Manifest} */ (
1658
+ JSON.parse(read(`${out}/server/.vite/manifest.json`))
1659
+ );
1635
1660
 
1636
- /** @type {Manifest} */
1637
- vite_server_manifest = JSON.parse(read(`${out}/server/.vite/manifest.json`));
1638
-
1639
- /** @type {BuildData} */
1640
- const build_data = {
1641
- app_dir: kit.appDir,
1642
- app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
1643
- manifest_data,
1644
- out_dir: out,
1645
- service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1646
- client: null,
1647
- server_manifest: vite_server_manifest
1648
- };
1661
+ const manifest_path = `${out}/server/manifest-full.js`;
1662
+ const assets_path = `${kit.appDir}/immutable/assets`;
1649
1663
 
1650
- const manifest_path = `${out}/server/manifest-full.js`;
1651
- fs.writeFileSync(
1652
- manifest_path,
1653
- `export const manifest = ${generate_manifest({
1654
- build_data,
1655
- prerendered: [],
1656
- relative_path: '.',
1657
- routes: manifest_data.routes,
1658
- remotes,
1659
- root
1660
- })};\n`
1661
- );
1664
+ /** @type {BuildData} */
1665
+ const build_data = {
1666
+ app_dir: kit.appDir,
1667
+ app_path: `${kit.paths.base.slice(1)}${kit.paths.base ? '/' : ''}${kit.appDir}`,
1668
+ manifest_data,
1669
+ out_dir: out,
1670
+ service_worker: service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable?
1671
+ client: null,
1672
+ server_manifest: vite_server_manifest
1673
+ };
1662
1674
 
1663
- const assets_path = `${kit.appDir}/immutable/assets`;
1664
-
1665
- // first, build server nodes without the client manifest so we can analyse it
1666
- build_server_nodes(
1667
- out,
1668
- kit,
1669
- manifest_data,
1670
- vite_server_manifest,
1671
- null,
1672
- assets_path,
1673
- server_chunks,
1674
- root
1675
- );
1675
+ fs.writeFileSync(
1676
+ manifest_path,
1677
+ `export const manifest = ${generate_manifest({
1678
+ build_data,
1679
+ prerendered: [],
1680
+ relative_path: '.',
1681
+ routes: manifest_data.routes,
1682
+ remotes,
1683
+ root
1684
+ })};\n`
1685
+ );
1676
1686
 
1677
- log.info('Analysing routes');
1678
-
1679
- const { metadata } = await analyse({
1680
- hash: kit.router.type === 'hash',
1681
- manifest_path,
1682
- manifest_data,
1683
- server_manifest: vite_server_manifest,
1684
- tracked_features,
1685
- env,
1686
- remotes,
1687
- vite_config_file: vite_config.configFile
1688
- });
1687
+ // first, build server nodes without the client manifest so we can analyse it
1688
+ build_server_nodes(
1689
+ out,
1690
+ kit,
1691
+ manifest_data,
1692
+ vite_server_manifest,
1693
+ null,
1694
+ assets_path,
1695
+ server_chunks,
1696
+ root
1697
+ );
1689
1698
 
1690
- build_metadata = metadata;
1699
+ log.info('Analysing routes');
1700
+
1701
+ const { metadata } = await analyse({
1702
+ hash: kit.router.type === 'hash',
1703
+ manifest_path,
1704
+ manifest_data,
1705
+ server_manifest: vite_server_manifest,
1706
+ tracked_features,
1707
+ env,
1708
+ remotes,
1709
+ vite_config_file: vite_config.configFile
1710
+ });
1711
+ build_metadata = metadata;
1691
1712
 
1692
- log.info('Building app');
1713
+ log.info('Building app');
1693
1714
 
1694
- // create client build
1695
- write_client_manifest(
1696
- kit,
1697
- manifest_data,
1698
- `${out_dir}/generated/client-optimized`,
1699
- metadata.nodes
1700
- );
1715
+ const server_assets = `${out}/server/${assets_path}`;
1716
+ const client_assets = `${out}/client/${assets_path}`;
1701
1717
 
1702
- const server_assets = `${out}/server/${assets_path}`;
1703
- const client_assets = `${out}/client/${assets_path}`;
1718
+ const skip_client_build = manifest_data.nodes.every(
1719
+ (node) => node.page_options?.csr === false
1720
+ );
1704
1721
 
1705
- const skip_client_build = manifest_data.nodes.every(
1706
- (node) => node.page_options?.csr === false
1707
- );
1722
+ if (skip_client_build) {
1723
+ copy(server_assets, client_assets);
1724
+ copy(kit.files.assets, `${out}/client`);
1725
+ } else {
1726
+ // ...and build the client
1727
+ write_client_manifest(
1728
+ kit,
1729
+ manifest_data,
1730
+ `${out_dir}/generated/client-optimized`,
1731
+ build_metadata.nodes
1732
+ );
1708
1733
 
1709
- if (!skip_client_build) {
1710
- const nodes = Object.values(build_metadata.nodes);
1734
+ // Through the finished analysis we can now check if any node has server or universal load functions
1735
+ const nodes = Object.values(build_metadata.nodes);
1736
+ const has_server_load = nodes.some((node) => node.has_server_load);
1737
+ const has_universal_load = nodes.some((node) => node.has_universal_load);
1711
1738
 
1712
- // Through the finished analysis we can now check if any node has server or universal load functions
1713
- const has_server_load = nodes.some((node) => node.has_server_load);
1714
- const has_universal_load = nodes.some((node) => node.has_universal_load);
1739
+ if (builder.environments.client.config.define) {
1740
+ builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
1741
+ s(has_server_load);
1742
+ builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
1743
+ s(has_universal_load);
1744
+ }
1715
1745
 
1716
- if (builder.environments.client.config.define) {
1717
- builder.environments.client.config.define.__SVELTEKIT_HAS_SERVER_LOAD__ =
1718
- s(has_server_load);
1719
- builder.environments.client.config.define.__SVELTEKIT_HAS_UNIVERSAL_LOAD__ =
1720
- s(has_universal_load);
1721
- }
1746
+ const client_build = await builder.build(builder.environments.client);
1747
+ const client_chunks = await normalise_build(
1748
+ builder.environments.client.name,
1749
+ client_build,
1750
+ watch_build_output
1751
+ );
1722
1752
 
1723
- const { output: client_chunks } = /** @type {Rolldown.RolldownOutput} */ (
1724
- await builder.build(builder.environments.client)
1725
- );
1753
+ // We use `build.ssrEmitAssets` so that asset URLs created from
1754
+ // imports in server-only modules correspond to files in the build,
1755
+ // but we don't want to copy over CSS imports as these are already
1756
+ // accounted for in the client bundle. In most cases it would be
1757
+ // a no-op, but for SSR builds `url(...)` paths are handled
1758
+ // differently (relative for client, absolute for server)
1759
+ // resulting in different hashes, and thus duplication
1760
+ const ssr_stylesheets = new Set(
1761
+ Object.values(vite_server_manifest)
1762
+ .map((chunk) => chunk.css ?? [])
1763
+ .flat()
1764
+ );
1726
1765
 
1727
- // We use `build.ssrEmitAssets` so that asset URLs created from
1728
- // imports in server-only modules correspond to files in the build,
1729
- // but we don't want to copy over CSS imports as these are already
1730
- // accounted for in the client bundle. In most cases it would be
1731
- // a no-op, but for SSR builds `url(...)` paths are handled
1732
- // differently (relative for client, absolute for server)
1733
- // resulting in different hashes, and thus duplication
1734
- const ssr_stylesheets = new Set(
1735
- Object.values(vite_server_manifest)
1736
- .map((chunk) => chunk.css ?? [])
1737
- .flat()
1738
- );
1766
+ if (fs.existsSync(server_assets)) {
1767
+ for (const file of fs.readdirSync(server_assets)) {
1768
+ const src = `${server_assets}/${file}`;
1769
+ const dest = `${client_assets}/${file}`;
1739
1770
 
1740
- if (fs.existsSync(server_assets)) {
1741
- for (const file of fs.readdirSync(server_assets)) {
1742
- const src = `${server_assets}/${file}`;
1743
- const dest = `${client_assets}/${file}`;
1771
+ if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1772
+ continue;
1773
+ }
1744
1774
 
1745
- if (fs.existsSync(dest) || ssr_stylesheets.has(`${assets_path}/${file}`)) {
1746
- continue;
1775
+ copy(src, dest);
1747
1776
  }
1777
+ }
1778
+
1779
+ vite_client_manifest = /** @type {Manifest} */ (
1780
+ JSON.parse(read(`${out}/client/.vite/manifest.json`))
1781
+ );
1748
1782
 
1749
- copy(src, dest);
1783
+ /**
1784
+ * @param {string} entry
1785
+ * @param {boolean} [add_dynamic_css]
1786
+ */
1787
+ const deps_of = (entry, add_dynamic_css = false) =>
1788
+ find_deps(
1789
+ /** @type {Manifest} */ (vite_client_manifest),
1790
+ posixify(path.relative(root, entry)),
1791
+ add_dynamic_css,
1792
+ root
1793
+ );
1794
+
1795
+ // the inline bundle and stylesheet are deleted further down, after
1796
+ // being inlined into the page, so they must not appear in `immutable`
1797
+ /** @type {Set<string>} */
1798
+ const inlined = new Set();
1799
+ /** @type {Rolldown.OutputAsset | undefined} */
1800
+ let inline_style;
1801
+
1802
+ if (kit.output.bundleStrategy === 'inline') {
1803
+ inline_style = /** @type {Rolldown.OutputAsset | undefined} */ (
1804
+ client_chunks.find(
1805
+ (chunk) =>
1806
+ chunk.type === 'asset' &&
1807
+ chunk.names.length === 1 &&
1808
+ chunk.names[0] === 'style.css'
1809
+ )
1810
+ );
1811
+
1812
+ inlined.add(deps_of(`${runtime_directory}/client/bundle.js`).file);
1813
+ if (inline_style) inlined.add(inline_style.fileName);
1750
1814
  }
1751
- }
1752
1815
 
1753
- const vite_manifest = (vite_client_manifest = JSON.parse(
1754
- read(`${out}/client/.vite/manifest.json`)
1755
- ));
1816
+ // Replace manifest placeholders in client output. `immutable` is
1817
+ // computed from the Vite client manifest, `assets` and `routes`
1818
+ // from `manifest_data`. `prerendered` is left as a placeholder
1819
+ // for now — it's replaced after prerendering completes.
1820
+ immutable = collect_immutable(vite_client_manifest, kit.appDir, inlined);
1756
1821
 
1757
- /**
1758
- * @param {string} entry
1759
- * @param {boolean} [add_dynamic_css]
1760
- */
1761
- const deps_of = (entry, add_dynamic_css = false) =>
1762
- find_deps(vite_manifest, posixify(path.relative(root, entry)), add_dynamic_css, root);
1822
+ replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
1823
+ immutable,
1824
+ assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1825
+ routes: get_manifest_routes(manifest_data.routes)
1826
+ });
1763
1827
 
1764
- // the inline bundle and stylesheet are deleted further down, after
1765
- // being inlined into the page, so they must not appear in `immutable`
1766
- /** @type {Set<string>} */
1767
- const inlined = new Set();
1768
- /** @type {Rolldown.OutputAsset | undefined} */
1769
- let inline_style;
1770
-
1771
- if (kit.output.bundleStrategy === 'inline') {
1772
- inline_style = /** @type {Rolldown.OutputAsset | undefined} */ (
1773
- client_chunks.find(
1774
- (chunk) =>
1775
- chunk.type === 'asset' && chunk.names.length === 1 && chunk.names[0] === 'style.css'
1776
- )
1828
+ // Now that the client build is done, replace the `build` sentinel
1829
+ // in the SSR output with the real build files
1830
+ replace_manifest_placeholder_strings(`${out}/server`, { immutable });
1831
+
1832
+ const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1833
+ (variable) => variable.public && !variable.static
1777
1834
  );
1778
1835
 
1779
- inlined.add(deps_of(`${runtime_directory}/client/bundle.js`).file);
1780
- if (inline_style) inlined.add(inline_style.fileName);
1781
- }
1836
+ // the app only depends on runtime public env if it imports `$app/env/public`
1837
+ // *and* at least one public env var is actually dynamic (non-static)
1838
+ const uses_env_dynamic_public =
1839
+ has_explicit_dynamic_public_env &&
1840
+ client_chunks.some(
1841
+ (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1842
+ );
1782
1843
 
1783
- // Replace manifest placeholders in client output. `immutable` is
1784
- // computed from the Vite client manifest, `assets` and `routes`
1785
- // from `manifest_data`. `prerendered` is left as a placeholder
1786
- // for now — it's replaced after prerendering completes.
1787
- immutable = collect_immutable(vite_manifest, kit.appDir, inlined);
1844
+ if (kit.output.bundleStrategy === 'split') {
1845
+ const start_entry = posixify(
1846
+ path.relative(root, `${runtime_directory}/client/entry.js`)
1847
+ );
1848
+ const start = find_deps(vite_client_manifest, start_entry, false, root);
1849
+ const runtime_entry = resolve_symlinks(vite_client_manifest, start_entry, root).chunk
1850
+ .dynamicImports?.[0]; // client/entry.js dynamically imports client/client-entry.js
1851
+ if (!runtime_entry) throw new Error('Could not find the client runtime chunk');
1852
+ const runtime = find_deps(vite_client_manifest, runtime_entry, false, root);
1853
+ const app = deps_of(`${out_dir}/generated/client-optimized/app.js`);
1854
+
1855
+ build_data.client = {
1856
+ start: start.file,
1857
+ app: app.file,
1858
+ imports: Array.from(
1859
+ new Set([
1860
+ ...start.imports,
1861
+ runtime.file,
1862
+ ...runtime.imports,
1863
+ app.file,
1864
+ ...app.imports
1865
+ ])
1866
+ ),
1867
+ stylesheets: [...start.stylesheets, ...runtime.stylesheets, ...app.stylesheets],
1868
+ fonts: [...start.fonts, ...runtime.fonts, ...app.fonts],
1869
+ uses_env_dynamic_public
1870
+ };
1788
1871
 
1789
- replace_manifest_placeholder_variables(client_chunks, `${out}/client`, {
1790
- immutable,
1791
- assets: manifest_data.assets.map((asset) => ({ path: asset.file })),
1792
- routes: get_manifest_routes(manifest_data.routes)
1793
- });
1872
+ // In case of server-side route resolution, we create a purpose-built route manifest that is
1873
+ // similar to that on the client, with as much information computed upfront so that we
1874
+ // don't need to include any code of the actual routes in the server bundle.
1875
+ if (kit.router.resolution === 'server') {
1876
+ const nodes = manifest_data.nodes.map((node, i) => {
1877
+ if (node.component || node.universal) {
1878
+ const entry = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1879
+ const deps = deps_of(entry, true);
1880
+ const file = resolve_symlinks(
1881
+ /** @type {Manifest} */ (vite_client_manifest),
1882
+ `${out_dir}/generated/client-optimized/nodes/${i}.js`,
1883
+ root
1884
+ ).chunk.file;
1885
+
1886
+ return { file, css: deps.stylesheets };
1887
+ }
1888
+ });
1889
+ build_data.client.nodes = nodes.map((node) => node?.file);
1890
+ build_data.client.css = nodes.map((node) => node?.css);
1891
+
1892
+ build_data.client.routes = compact(
1893
+ manifest_data.routes.map((route) => {
1894
+ if (!route.page) return;
1895
+
1896
+ return {
1897
+ id: route.id,
1898
+ pattern: route.pattern,
1899
+ params: route.params,
1900
+ layouts: route.page.layouts.map((l) =>
1901
+ l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1902
+ ),
1903
+ errors: route.page.errors,
1904
+ leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1905
+ };
1906
+ })
1907
+ );
1908
+ }
1909
+ } else {
1910
+ const start = deps_of(`${runtime_directory}/client/bundle.js`);
1911
+
1912
+ build_data.client = {
1913
+ start: start.file,
1914
+ imports: start.imports,
1915
+ stylesheets: start.stylesheets,
1916
+ fonts: start.fonts,
1917
+ uses_env_dynamic_public
1918
+ };
1794
1919
 
1795
- // Now that the client build is done, replace the `build` sentinel
1796
- // in the SSR output with the real build files
1797
- replace_manifest_placeholder_strings(`${out}/server`, { immutable });
1920
+ if (kit.output.bundleStrategy === 'inline') {
1921
+ build_data.client.inline = {
1922
+ script: read(`${out}/client/${start.file}`),
1923
+ style: /** @type {string | undefined} */ (inline_style?.source)
1924
+ };
1925
+
1926
+ // the bundle and stylesheet are inlined into the page, so the
1927
+ // emitted files are never loaded
1928
+ fs.unlinkSync(`${out}/client/${start.file}`);
1929
+ fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1930
+ if (inline_style) fs.unlinkSync(`${out}/client/${inline_style.fileName}`);
1931
+ }
1932
+ }
1798
1933
 
1799
- const has_explicit_dynamic_public_env = Object.values(explicit_env_config ?? {}).some(
1800
- (variable) => variable.public && !variable.static
1801
- );
1934
+ // regenerate manifest now that we have client entry...
1935
+ fs.writeFileSync(
1936
+ manifest_path,
1937
+ `export const manifest = ${generate_manifest({
1938
+ build_data,
1939
+ prerendered: [],
1940
+ relative_path: '.',
1941
+ routes: manifest_data.routes,
1942
+ remotes,
1943
+ root
1944
+ })};\n`
1945
+ );
1802
1946
 
1803
- // the app only depends on runtime public env if it imports `$app/env/public`
1804
- // *and* at least one public env var is actually dynamic (non-static)
1805
- const uses_env_dynamic_public =
1806
- has_explicit_dynamic_public_env &&
1807
- client_chunks.some(
1808
- (chunk) => chunk.type === 'chunk' && chunk.modules[sveltekit_env_public_client]
1947
+ // regenerate nodes with the client manifest...
1948
+ build_server_nodes(
1949
+ out,
1950
+ kit,
1951
+ manifest_data,
1952
+ vite_server_manifest,
1953
+ vite_client_manifest,
1954
+ assets_path,
1955
+ client_chunks,
1956
+ root
1809
1957
  );
1958
+ }
1810
1959
 
1811
- if (svelte_config.kit.output.bundleStrategy === 'split') {
1812
- const start_entry = posixify(path.relative(root, `${runtime_directory}/client/entry.js`));
1813
- const start = find_deps(vite_manifest, start_entry, false, root);
1814
- const runtime_entry = resolve_symlinks(vite_manifest, start_entry, root).chunk
1815
- .dynamicImports?.[0]; // client/entry.js dynamically imports client/client-entry.js
1816
- if (!runtime_entry) throw new Error('Could not find the client runtime chunk');
1817
- const runtime = find_deps(vite_manifest, runtime_entry, false, root);
1818
- const app = deps_of(`${out_dir}/generated/client-optimized/app.js`);
1819
-
1820
- build_data.client = {
1821
- start: start.file,
1822
- app: app.file,
1823
- imports: Array.from(
1824
- new Set([
1825
- ...start.imports,
1826
- runtime.file,
1827
- ...runtime.imports,
1828
- app.file,
1829
- ...app.imports
1830
- ])
1831
- ),
1832
- stylesheets: [...start.stylesheets, ...runtime.stylesheets, ...app.stylesheets],
1833
- fonts: [...start.fonts, ...runtime.fonts, ...app.fonts],
1834
- uses_env_dynamic_public
1835
- };
1960
+ // ...and prerender
1961
+ let prerender_results;
1962
+ try {
1963
+ prerender_results = await prerender({
1964
+ hash: kit.router.type === 'hash',
1965
+ out,
1966
+ manifest_path,
1967
+ metadata,
1968
+ verbose,
1969
+ env,
1970
+ vite_config_file: vite_config.configFile,
1971
+ is_tty: process.stdout.isTTY
1972
+ });
1836
1973
 
1837
- // In case of server-side route resolution, we create a purpose-built route manifest that is
1838
- // similar to that on the client, with as much information computed upfront so that we
1839
- // don't need to include any code of the actual routes in the server bundle.
1840
- if (svelte_config.kit.router.resolution === 'server') {
1841
- const nodes = manifest_data.nodes.map((node, i) => {
1842
- if (node.component || node.universal) {
1843
- const entry = `${out_dir}/generated/client-optimized/nodes/${i}.js`;
1844
- const deps = deps_of(entry, true);
1845
- const file = resolve_symlinks(
1846
- vite_manifest,
1847
- `${out_dir}/generated/client-optimized/nodes/${i}.js`,
1848
- root
1849
- ).chunk.file;
1850
-
1851
- return { file, css: deps.stylesheets };
1852
- }
1853
- });
1854
- build_data.client.nodes = nodes.map((node) => node?.file);
1855
- build_data.client.css = nodes.map((node) => node?.css);
1856
-
1857
- build_data.client.routes = compact(
1858
- manifest_data.routes.map((route) => {
1859
- if (!route.page) return;
1860
-
1861
- return {
1862
- id: route.id,
1863
- pattern: route.pattern,
1864
- params: route.params,
1865
- layouts: route.page.layouts.map((l) =>
1866
- l !== undefined ? [metadata.nodes[l].has_server_load, l] : undefined
1867
- ),
1868
- errors: route.page.errors,
1869
- leaf: [metadata.nodes[route.page.leaf].has_server_load, route.page.leaf]
1870
- };
1871
- })
1872
- );
1974
+ // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
1975
+ await new Promise((f) => setTimeout(f, 0));
1976
+ } catch (e) {
1977
+ if (e instanceof Error && e.message === '__handled__') {
1978
+ // error details are already logged inside `prerender`, don't duplicate them
1979
+ throw stackless('Prerendering failed');
1980
+ } else {
1981
+ // Unforeseen error, rethrow as-is
1982
+ throw e;
1873
1983
  }
1874
- } else {
1875
- const start = deps_of(`${runtime_directory}/client/bundle.js`);
1876
-
1877
- build_data.client = {
1878
- start: start.file,
1879
- imports: start.imports,
1880
- stylesheets: start.stylesheets,
1881
- fonts: start.fonts,
1882
- uses_env_dynamic_public
1883
- };
1984
+ }
1884
1985
 
1885
- if (svelte_config.kit.output.bundleStrategy === 'inline') {
1886
- build_data.client.inline = {
1887
- script: read(`${out}/client/${start.file}`),
1888
- style: /** @type {string | undefined} */ (inline_style?.source)
1889
- };
1986
+ prerendered = prerender_results.prerendered;
1890
1987
 
1891
- // the bundle and stylesheet are inlined into the page, so the
1892
- // emitted files are never loaded
1893
- fs.unlinkSync(`${out}/client/${start.file}`);
1894
- fs.rmSync(`${out}/client/${start.file}.map`, { force: true });
1895
- if (inline_style) fs.unlinkSync(`${out}/client/${inline_style.fileName}`);
1896
- }
1988
+ // Replace the `prerendered` sentinel in both SSR and client output
1989
+ // with the real prerendered paths. The other sentinels (`build`)
1990
+ // were already replaced after the client build.
1991
+ const prerendered_paths = prerendered.paths.map((p) => {
1992
+ return { path: p.replace(kit.paths.base, '').slice(1) };
1993
+ });
1994
+
1995
+ replace_manifest_placeholder_strings(`${out}/server`, { prerendered: prerendered_paths });
1996
+ replace_manifest_placeholder_strings(`${out}/client`, { prerendered: prerendered_paths });
1997
+
1998
+ // For `inline` strategy, the entry file was deleted and read into
1999
+ // `build_data.client.inline.script` — replace the sentinel there too
2000
+ if (build_data.client?.inline?.script) {
2001
+ build_data.client.inline.script = build_data.client.inline.script.replaceAll(
2002
+ '"__sveltekit_manifest_prerendered__"',
2003
+ JSON.stringify(prerendered_paths)
2004
+ );
1897
2005
  }
1898
2006
 
1899
- // regenerate manifest now that we have client entry...
2007
+ // generate a new manifest that doesn't include prerendered pages
1900
2008
  fs.writeFileSync(
1901
- manifest_path,
2009
+ `${out}/server/manifest.js`,
1902
2010
  `export const manifest = ${generate_manifest({
1903
2011
  build_data,
1904
- prerendered: [],
2012
+ prerendered: prerendered.paths,
1905
2013
  relative_path: '.',
1906
- routes: manifest_data.routes,
2014
+ routes: manifest_data.routes.filter(
2015
+ (route) => prerender_results.prerender_map.get(route.id) !== true
2016
+ ),
1907
2017
  remotes,
1908
2018
  root
1909
2019
  })};\n`
1910
2020
  );
1911
2021
 
1912
- // regenerate nodes with the client manifest...
1913
- build_server_nodes(
2022
+ await treeshake_prerendered_remotes(
2023
+ vite,
1914
2024
  out,
1915
- kit,
1916
- manifest_data,
1917
- vite_server_manifest,
1918
- vite_client_manifest,
1919
- assets_path,
1920
- client_chunks,
1921
- root
2025
+ remotes,
2026
+ remote_original_by_hash,
2027
+ metadata,
2028
+ process.cwd(),
2029
+ server_chunks,
2030
+ vite_config.build.sourcemap
1922
2031
  );
1923
- } else {
1924
- copy(server_assets, client_assets);
1925
- copy(kit.files.assets, `${out}/client`);
1926
- }
1927
2032
 
1928
- // ...and prerender
1929
- let prerender_results;
1930
- try {
1931
- prerender_results = await prerender({
1932
- hash: kit.router.type === 'hash',
1933
- out,
1934
- manifest_path,
1935
- metadata,
1936
- verbose,
1937
- env,
1938
- vite_config_file: vite_config.configFile
1939
- });
2033
+ // defer until after other buildApp hooks have run
2034
+ finalise = async () => {
2035
+ // defer creating the service worker to avoid other plugins from
2036
+ // overwriting it if they run a client environment build
2037
+ if (service_worker_entry_file) {
2038
+ log.info('Building service worker');
2039
+
2040
+ // mirror client settings that we couldn't set per environment in the config hook
2041
+ builder.environments.serviceWorker.config.define =
2042
+ builder.environments.client.config.define;
2043
+ builder.environments.serviceWorker.config.resolve.alias = [
2044
+ ...get_config_aliases(kit, vite_config.root)
2045
+ ];
2046
+
2047
+ // we have to overwrite this because it can't be configured per environment in the config hook
2048
+ builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
2049
+ return {
2050
+ runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
2051
+ };
2052
+ };
1940
2053
 
1941
- // this silly hack is necessary to ensure that stderr from prerender is flushed before we continue
1942
- await new Promise((f) => setTimeout(f, 0));
1943
- } catch (e) {
1944
- if (e instanceof Error && e.message === '__handled__') {
1945
- // error details are already logged inside `prerender`, don't duplicate them
1946
- throw stackless('Prerendering failed');
1947
- } else {
1948
- // Unforeseen error, rethrow as-is
1949
- throw e;
1950
- }
1951
- }
2054
+ const service_worker_build = await builder.build(builder.environments.serviceWorker);
2055
+ await normalise_build(
2056
+ builder.environments.serviceWorker.name,
2057
+ service_worker_build,
2058
+ watch_build_output
2059
+ );
2060
+ }
1952
2061
 
1953
- prerendered = prerender_results.prerendered;
2062
+ console.log(
2063
+ `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
2064
+ );
1954
2065
 
1955
- // Replace the `prerendered` sentinel in both SSR and client output
1956
- // with the real prerendered paths. The other sentinels (`build`)
1957
- // were already replaced after the client build.
1958
- const prerendered_paths = prerendered.paths.map((p) => {
1959
- return { path: p.replace(kit.paths.base, '').slice(1) };
1960
- });
2066
+ if (kit.adapter) {
2067
+ await adapt(
2068
+ svelte_config,
2069
+ build_data,
2070
+ metadata,
2071
+ prerendered,
2072
+ prerender_results.prerender_map,
2073
+ log,
2074
+ remotes,
2075
+ vite_config,
2076
+ explicit_env_config
2077
+ );
2078
+ } else {
2079
+ log.warn('\nNo adapter specified');
1961
2080
 
1962
- replace_manifest_placeholder_strings(`${out}/server`, { prerendered: prerendered_paths });
1963
- replace_manifest_placeholder_strings(`${out}/client`, { prerendered: prerendered_paths });
2081
+ const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
2082
+ console.log(
2083
+ `See ${link} to learn how to configure your app to run on the platform of your choosing`
2084
+ );
2085
+ }
2086
+ };
2087
+ };
1964
2088
 
1965
- // For `inline` strategy, the entry file was deleted and read into
1966
- // `build_data.client.inline.script` replace the sentinel there too
1967
- if (build_data.client?.inline?.script) {
1968
- build_data.client.inline.script = build_data.client.inline.script.replaceAll(
1969
- '"__sveltekit_manifest_prerendered__"',
1970
- JSON.stringify(prerendered_paths)
1971
- );
2089
+ // `vite build`
2090
+ ssr_build = Array.isArray(ssr_build) ? ssr_build[0] : ssr_build;
2091
+ if ('output' in ssr_build) {
2092
+ await load_and_validate_params({
2093
+ routes: manifest_data.routes,
2094
+ params_path: manifest_data.params,
2095
+ root
2096
+ });
2097
+
2098
+ return await process_ssr_build(ssr_build.output);
1972
2099
  }
1973
2100
 
1974
- await treeshake_prerendered_remotes(
1975
- vite,
1976
- out,
1977
- remotes,
1978
- remote_original_by_hash,
1979
- metadata,
1980
- process.cwd(),
1981
- server_chunks,
1982
- vite_config.build.sourcemap
1983
- );
2101
+ // `vite build --watch`
2102
+ let rebuild = false;
1984
2103
 
1985
- // generate a new manifest that doesn't include prerendered pages
1986
- fs.writeFileSync(
1987
- `${out}/server/manifest.js`,
1988
- `export const manifest = ${generate_manifest({
1989
- build_data,
1990
- prerendered: prerendered.paths,
1991
- relative_path: '.',
1992
- routes: manifest_data.routes.filter(
1993
- (route) => prerender_results.prerender_map.get(route.id) !== true
1994
- ),
1995
- remotes,
1996
- root
1997
- })};\n`
1998
- );
2104
+ const before_ssr_build_rerun = async () => {
2105
+ rebuild = true;
1999
2106
 
2000
- // defer the adapt step to run after any buildApp hooks the adapter might have
2001
- finalise = async () => {
2002
- // defer creating the service worker too because other plugins might build
2003
- // the client environment again and overwrite our service worker which
2004
- // outputs to the same directory
2005
- if (service_worker_entry_file) {
2006
- log.info('Building service worker');
2107
+ // these are set once per plugin initialisation or when the config hook
2108
+ // runs. However, those don't re-run during watch mode. So, we need to
2109
+ // re-initialise them manually here
2110
+ manifest_data = sync.all(svelte_config, root).manifest_data;
2007
2111
 
2008
- // mirror client settings that we couldn't set per environment in the config hook
2009
- builder.environments.serviceWorker.config.define =
2010
- builder.environments.client.config.define;
2011
- builder.environments.serviceWorker.config.resolve.alias = [
2012
- ...get_config_aliases(kit, vite_config.root)
2013
- ];
2112
+ tracked_features = {};
2014
2113
 
2015
- // we have to overwrite this because it can't be configured per environment in the config hook
2016
- builder.environments.serviceWorker.config.experimental.renderBuiltUrl = (filename) => {
2017
- return {
2018
- runtime: `new URL(${JSON.stringify(filename)}, location.href).pathname`
2019
- };
2020
- };
2114
+ remotes = [];
2115
+ remote_original_by_hash.clear();
2116
+ emitted_remote_hashes.clear();
2021
2117
 
2022
- await builder.build(builder.environments.serviceWorker);
2023
- }
2118
+ immutable = null;
2119
+ manifest_data_code = null;
2120
+
2121
+ finalise = null;
2024
2122
 
2025
- console.log(
2026
- `\nRun ${styleText(['bold', 'cyan'], 'npm run preview')} to preview your production build locally.`
2123
+ fs.mkdirSync(out, { recursive: true });
2124
+
2125
+ explicit_env_entry = resolve_explicit_env_entry(kit);
2126
+ explicit_env_config = await sync.env(
2127
+ vite,
2128
+ kit,
2129
+ explicit_env_entry,
2130
+ vite_config.root,
2131
+ vite_config.mode
2027
2132
  );
2028
2133
 
2029
- if (kit.adapter) {
2030
- const { adapt } = await import('../../core/adapt/index.js');
2031
- await adapt(
2032
- svelte_config,
2033
- build_data,
2034
- metadata,
2035
- prerendered,
2036
- prerender_results.prerender_map,
2037
- log,
2038
- remotes,
2039
- vite_config,
2040
- explicit_env_config
2041
- );
2042
- } else {
2043
- log.warn('\nNo adapter specified');
2134
+ await load_and_validate_params({
2135
+ routes: manifest_data.routes,
2136
+ params_path: manifest_data.params,
2137
+ root
2138
+ });
2139
+ };
2044
2140
 
2045
- const link = styleText(['bold', 'cyan'], 'https://svelte.dev/docs/kit/adapters');
2046
- console.log(
2047
- `See ${link} to learn how to configure your app to run on the platform of your choosing`
2048
- );
2141
+ ssr_build.on('change', before_ssr_build_rerun);
2142
+ ssr_build.on('restart', before_ssr_build_rerun);
2143
+
2144
+ /** @type {PromiseWithResolvers<void>} */
2145
+ const task = Promise.withResolvers();
2146
+
2147
+ ssr_build.on('event', async (event) => {
2148
+ if (event.code === 'ERROR') {
2149
+ return task.reject();
2049
2150
  }
2050
- };
2151
+
2152
+ if (event.code === 'BUNDLE_END') {
2153
+ try {
2154
+ await process_ssr_build(
2155
+ /** @type {Rolldown.RolldownOutput['output']} */ (
2156
+ watch_build_output.get(builder.environments.ssr.name)
2157
+ )
2158
+ );
2159
+ // buildApp hooks don't rerun in watch mode so we need to run
2160
+ // the deferred steps here on subsequent builds
2161
+ if (rebuild) await finalise?.();
2162
+ } catch (e) {
2163
+ return task.reject(e);
2164
+ } finally {
2165
+ await event.result.close();
2166
+ }
2167
+ return task.resolve();
2168
+ }
2169
+ });
2170
+
2171
+ await task.promise;
2051
2172
  }
2052
2173
  };
2053
2174
 
@@ -2340,3 +2461,39 @@ const replace_manifest_placeholder_strings = (dir, values) => {
2340
2461
  }
2341
2462
  }
2342
2463
  };
2464
+
2465
+ /**
2466
+ * Normalises the build output to a consistent format, handling watch mode and multiple environments
2467
+ * @param {string} name The name of the environment
2468
+ * @param {Rolldown.RolldownOutput | Rolldown.RolldownOutput[] | Rolldown.RolldownWatcher} build The return value of builder.build
2469
+ * @param {Map<string, Rolldown.RolldownOutput['output']>} build_output_map
2470
+ * @returns {Promise<Rolldown.RolldownOutput['output']>}
2471
+ */
2472
+ async function normalise_build(name, build, build_output_map) {
2473
+ if ('output' in build) {
2474
+ return build.output;
2475
+ }
2476
+
2477
+ if (Array.isArray(build)) {
2478
+ return build[0].output;
2479
+ }
2480
+
2481
+ /** @type {PromiseWithResolvers<void>} */
2482
+ const bundling = Promise.withResolvers();
2483
+
2484
+ build.on('event', async (event) => {
2485
+ if (event.code === 'ERROR') {
2486
+ await build.close();
2487
+ return bundling.reject(event.error);
2488
+ }
2489
+
2490
+ if (event.code === 'BUNDLE_END') {
2491
+ await build.close();
2492
+ return bundling.resolve();
2493
+ }
2494
+ });
2495
+
2496
+ await bundling.promise;
2497
+
2498
+ return /** @type {Rolldown.RolldownOutput['output']} */ (build_output_map.get(name));
2499
+ }