diff --git a/.changeset/clear-wolves-worry.md b/.changeset/clear-wolves-worry.md new file mode 100644 index 000000000000..7c10119d5094 --- /dev/null +++ b/.changeset/clear-wolves-worry.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': minor +--- + +feat: lazy discovery of remote functions diff --git a/packages/kit/src/core/adapt/builder.js b/packages/kit/src/core/adapt/builder.js index 3d0de4472e73..01904d9501eb 100644 --- a/packages/kit/src/core/adapt/builder.js +++ b/packages/kit/src/core/adapt/builder.js @@ -1,7 +1,7 @@ /** @import { Builder } from '@sveltejs/kit' */ /** @import { ResolvedConfig } from 'vite' */ /** @import { RouteDefinition } from '@sveltejs/kit' */ -/** @import { RouteData, ValidatedConfig, BuildData, ServerMetadata, ServerMetadataRoute, Prerendered, PrerenderMap, Logger } from 'types' */ +/** @import { RouteData, ValidatedConfig, BuildData, ServerMetadata, ServerMetadataRoute, Prerendered, PrerenderMap, Logger, RemoteChunk } from 'types' */ import colors from 'kleur'; import { createReadStream, createWriteStream, existsSync, statSync } from 'node:fs'; import { extname, resolve, join, dirname, relative } from 'node:path'; @@ -32,6 +32,7 @@ const extensions = ['.html', '.js', '.mjs', '.json', '.css', '.svg', '.xml', '.w * prerender_map: PrerenderMap; * log: Logger; * vite_config: ResolvedConfig; + * remotes: RemoteChunk[] * }} opts * @returns {Builder} */ @@ -43,7 +44,8 @@ export function create_builder({ prerendered, prerender_map, log, - vite_config + vite_config, + remotes }) { /** @type {Map} */ const lookup = new Map(); @@ -145,7 +147,8 @@ export function create_builder({ build_data, prerendered: [], relative_path: relativePath, - routes: Array.from(filtered) + routes: Array.from(filtered), + remotes }) }); } @@ -195,7 +198,8 @@ export function create_builder({ relative_path: relativePath, routes: subset ? subset.map((route) => /** @type {import('types').RouteData} */ (lookup.get(route))) - : route_data.filter((route) => prerender_map.get(route.id) !== true) + : route_data.filter((route) => prerender_map.get(route.id) !== true), + remotes }); }, diff --git a/packages/kit/src/core/adapt/index.js b/packages/kit/src/core/adapt/index.js index 3cfe52753248..48d14116369e 100644 --- a/packages/kit/src/core/adapt/index.js +++ b/packages/kit/src/core/adapt/index.js @@ -8,6 +8,7 @@ import { create_builder } from './builder.js'; * @param {import('types').Prerendered} prerendered * @param {import('types').PrerenderMap} prerender_map * @param {import('types').Logger} log + * @param {import('types').RemoteChunk[]} remotes * @param {import('vite').ResolvedConfig} vite_config */ export async function adapt( @@ -17,6 +18,7 @@ export async function adapt( prerendered, prerender_map, log, + remotes, vite_config ) { // This is only called when adapter is truthy, so the cast is safe @@ -32,6 +34,7 @@ export async function adapt( prerendered, prerender_map, log, + remotes, vite_config }); diff --git a/packages/kit/src/core/generate_manifest/index.js b/packages/kit/src/core/generate_manifest/index.js index 8b775584c3a1..6b2445aa2107 100644 --- a/packages/kit/src/core/generate_manifest/index.js +++ b/packages/kit/src/core/generate_manifest/index.js @@ -1,3 +1,4 @@ +/** @import { RemoteChunk } from 'types' */ import fs from 'node:fs'; import path from 'node:path'; import * as mime from 'mrmime'; @@ -18,9 +19,10 @@ import { uneval } from 'devalue'; * prerendered: string[]; * relative_path: string; * routes: import('types').RouteData[]; + * remotes: RemoteChunk[]; * }} opts */ -export function generate_manifest({ build_data, prerendered, relative_path, routes }) { +export function generate_manifest({ build_data, prerendered, relative_path, routes, remotes }) { /** * @type {Map} The new index of each node in the filtered nodes array */ @@ -101,7 +103,7 @@ export function generate_manifest({ build_data, prerendered, relative_path, rout ${(node_paths).map(loader).join(',\n')} ], remotes: { - ${build_data.manifest_data.remotes.map((remote) => `'${remote.hash}': ${loader(join_relative(relative_path, resolve_symlinks(build_data.server_manifest, remote.file).chunk.file))}`).join(',\n')} + ${remotes.map((remote) => `'${remote.hash}': ${loader(join_relative(relative_path, `chunks/remote-${remote.hash}.js`))}`).join(',\n')} }, routes: [ ${routes.map(route => { diff --git a/packages/kit/src/core/postbuild/analyse.js b/packages/kit/src/core/postbuild/analyse.js index e991996eeb42..d1c4800d1161 100644 --- a/packages/kit/src/core/postbuild/analyse.js +++ b/packages/kit/src/core/postbuild/analyse.js @@ -1,3 +1,4 @@ +/** @import { RemoteChunk } from 'types' */ import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; import { validate_server_exports } from '../../utils/exports.js'; @@ -11,7 +12,6 @@ import { check_feature } from '../../utils/features.js'; import { createReadableStream } from '@sveltejs/kit/node'; import { PageNodes } from '../../utils/page_nodes.js'; import { build_server_nodes } from '../../exports/vite/build/build_server.js'; -import { validate_remote_functions } from '@sveltejs/kit/internal'; export default forked(import.meta.url, analyse); @@ -25,6 +25,7 @@ export default forked(import.meta.url, analyse); * env: Record; * out: string; * output_config: import('types').RecursiveRequired; + * remotes: RemoteChunk[]; * }} opts */ async function analyse({ @@ -35,7 +36,8 @@ async function analyse({ tracked_features, env, out, - output_config + output_config, + remotes }) { /** @type {import('@sveltejs/kit').SSRManifest} */ const manifest = (await import(pathToFileURL(manifest_path).href)).manifest; @@ -166,16 +168,14 @@ async function analyse({ } // analyse remotes - for (const remote of manifest_data.remotes) { + for (const remote of remotes) { const loader = manifest._.remotes[remote.hash]; - const module = await loader(); - - validate_remote_functions(module, remote.file); + const { default: functions } = await loader(); const exports = new Map(); - for (const name in module) { - const info = /** @type {import('types').RemoteInfo} */ (module[name].__); + for (const name in functions) { + const info = /** @type {import('types').RemoteInfo} */ (functions[name].__); const type = info.type; exports.set(name, { diff --git a/packages/kit/src/core/postbuild/prerender.js b/packages/kit/src/core/postbuild/prerender.js index 0e0b2e9b653a..adb45960725a 100644 --- a/packages/kit/src/core/postbuild/prerender.js +++ b/packages/kit/src/core/postbuild/prerender.js @@ -495,7 +495,7 @@ async function prerender({ hash, out, manifest_path, metadata, verbose, env }) { for (const loader of Object.values(manifest._.remotes)) { const module = await loader(); - for (const fn of Object.values(module)) { + for (const fn of Object.values(module.default)) { if (fn?.__?.type === 'prerender') { prerender_functions.push(fn.__); should_prerender = true; diff --git a/packages/kit/src/core/sync/create_manifest_data/index.js b/packages/kit/src/core/sync/create_manifest_data/index.js index efc55fc5c574..b7c5e93d658d 100644 --- a/packages/kit/src/core/sync/create_manifest_data/index.js +++ b/packages/kit/src/core/sync/create_manifest_data/index.js @@ -4,11 +4,10 @@ import process from 'node:process'; import colors from 'kleur'; import { lookup } from 'mrmime'; import { list_files, runtime_directory } from '../../utils.js'; -import { posixify, resolve_entry, walk } from '../../../utils/filesystem.js'; +import { posixify, resolve_entry } from '../../../utils/filesystem.js'; import { parse_route_id } from '../../../utils/routing.js'; import { sort_routes } from './sort.js'; import { isSvelte5Plus } from '../utils.js'; -import { hash } from '../../../utils/hash.js'; /** * Generates the manifest data used for the client-side manifest and types generation. @@ -28,7 +27,6 @@ export default function create_manifest_data({ const hooks = create_hooks(config, cwd); const matchers = create_matchers(config, cwd); const { nodes, routes } = create_routes_and_nodes(cwd, config, fallback); - const remotes = create_remotes(config, cwd); for (const route of routes) { for (const param of route.params) { @@ -43,7 +41,6 @@ export default function create_manifest_data({ hooks, matchers, nodes, - remotes, routes }; } @@ -468,33 +465,6 @@ function create_routes_and_nodes(cwd, config, fallback) { }; } -/** - * @param {import('types').ValidatedConfig} config - * @param {string} cwd - */ -function create_remotes(config, cwd) { - if (!config.kit.experimental.remoteFunctions) return []; - - const extensions = config.kit.moduleExtensions.map((ext) => `.remote${ext}`); - - /** @type {import('types').ManifestData['remotes']} */ - const remotes = []; - - // TODO could files live in other directories, including node_modules? - for (const file of walk(config.kit.files.src)) { - if (extensions.some((ext) => file.endsWith(ext))) { - const posixified = posixify(path.relative(cwd, `${config.kit.files.src}/${file}`)); - - remotes.push({ - hash: hash(posixified), - file: posixified - }); - } - } - - return remotes; -} - /** * @param {string} project_relative * @param {string} file diff --git a/packages/kit/src/exports/internal/index.js b/packages/kit/src/exports/internal/index.js index c358bca93251..b87448b30914 100644 --- a/packages/kit/src/exports/internal/index.js +++ b/packages/kit/src/exports/internal/index.js @@ -62,4 +62,4 @@ export class ActionFailure { } } -export { validate_remote_functions } from './remote-functions.js'; +export { init_remote_functions } from './remote-functions.js'; diff --git a/packages/kit/src/exports/internal/remote-functions.js b/packages/kit/src/exports/internal/remote-functions.js index bd0893375046..ae83ed5d3d2b 100644 --- a/packages/kit/src/exports/internal/remote-functions.js +++ b/packages/kit/src/exports/internal/remote-functions.js @@ -1,27 +1,28 @@ +/** @import { RemoteInfo } from 'types' */ + +/** @type {RemoteInfo['type'][]} */ +const types = ['command', 'form', 'prerender', 'query', 'query_batch']; + /** * @param {Record} module * @param {string} file + * @param {string} hash */ -export function validate_remote_functions(module, file) { +export function init_remote_functions(module, file, hash) { if (module.default) { throw new Error( `Cannot export \`default\` from a remote module (${file}) — please use named exports instead` ); } - for (const name in module) { - const type = /** @type {import('types').RemoteInfo['type']} */ (module[name]?.__?.type); - - if ( - type !== 'form' && - type !== 'command' && - type !== 'query' && - type !== 'query_batch' && - type !== 'prerender' - ) { + for (const [name, fn] of Object.entries(module)) { + if (!types.includes(fn?.__?.type)) { throw new Error( `\`${name}\` exported from ${file} is invalid — all exports from this file must be remote functions` ); } + + fn.__.id = `${hash}/${name}`; + fn.__.name = name; } } diff --git a/packages/kit/src/exports/vite/build/build_remote.js b/packages/kit/src/exports/vite/build/build_remote.js deleted file mode 100644 index 668859858810..000000000000 --- a/packages/kit/src/exports/vite/build/build_remote.js +++ /dev/null @@ -1,129 +0,0 @@ -/** @import { ManifestData, ServerMetadata } from 'types' */ -import fs from 'node:fs'; -import path from 'node:path'; -import { posixify } from '../../../utils/filesystem.js'; -import { dedent } from '../../../core/sync/utils.js'; -import { import_peer } from '../../../utils/import.js'; - -/** - * Moves the remote files to a sibling file and rewrites the original remote file to import from that sibling file, - * enhancing the remote functions with their hashed ID. - * This is not done through a self-import like during DEV because we want to treeshake prerendered remote functions - * later, which wouldn't work if we do a self-import and iterate over all exports (since we're reading them then). - * @param {string} out - * @param {ManifestData} manifest_data - */ -export function build_remotes(out, manifest_data) { - const dir = `${out}/server/remote`; - - for (const remote of manifest_data.remotes) { - const entry = `${dir}/${remote.hash}.js`; - const tmp = `${remote.hash}.tmp.js`; - - fs.renameSync(entry, `${dir}/${tmp}`); - fs.writeFileSync( - entry, - dedent` - import * as $$_self_$$ from './${tmp}'; - - for (const [name, fn] of Object.entries($$_self_$$)) { - fn.__.id = '${remote.hash}/' + name; - fn.__.name = name; - } - - export * from './${tmp}'; - ` - ); - } -} - - -/** - * For each remote module, checks if there are treeshakeable prerendered remote functions, - * then accomplishes the treeshaking by rewriting the remote files to only include the non-prerendered imports, - * replacing the prerendered remote functions with a dummy function that should never be called, - * and doing a Vite build. This will not treeshake perfectly yet as everything except the remote files are treated as external, - * so it will not go into those files to check what can be treeshaken inside them. - * @param {string} out - * @param {ManifestData} manifest_data - * @param {ServerMetadata} metadata - */ -export async function treeshake_prerendered_remotes(out, manifest_data, metadata) { - if (manifest_data.remotes.length === 0) { - return; - } - - const dir = posixify(`${out}/server/remote`); - - const vite = /** @type {typeof import('vite')} */ (await import_peer('vite')); - const remote_entry = posixify(`${out}/server/remote-entry.js`); - - const prefix = 'optimized/'; - - const input = { - // include this file in the bundle, so that Rollup understands - // that functions like `prerender` are side-effect free - [path.basename(remote_entry.slice(0, -3))]: remote_entry - }; - - for (const remote of manifest_data.remotes) { - const exports = metadata.remotes.get(remote.hash); - if (!exports) throw new Error('An impossible situation occurred'); - - /** @type {string[]} */ - const dynamic = []; - - /** @type {string[]} */ - const prerendered = []; - - for (const [name, value] of exports) { - (value.dynamic ? dynamic : prerendered).push(name); - } - - const remote_file = posixify(`${dir}/${remote.hash}.js`); - - fs.writeFileSync( - remote_file, - dedent` - import { ${dynamic.join(', ')} } from './${remote.hash}.tmp.js'; - import { prerender } from '../${path.basename(remote_entry)}'; - - ${prerendered.map((name) => `export const ${name} = prerender('unchecked', () => { throw new Error('Unexpectedly called prerender function. Did you forget to set { dynamic: true } ?') });`).join('\n')} - - for (const [name, fn] of Object.entries({ ${Array.from(exports.keys()).join(', ')} })) { - fn.__.id = '${remote.hash}/' + name; - fn.__.name = name; - } - - export { ${dynamic.join(', ')} }; - ` - ); - - input[prefix + remote.hash] = remote_file; - } - - const bundle = /** @type {import('vite').Rollup.RollupOutput} */ (await vite.build({ - configFile: false, - build: { - write: false, - ssr: true, - rollupOptions: { - external: (id) => { - if (id[0] === '.') return; - return !id.startsWith(dir); - }, - input - } - } - })); - - for (const chunk of bundle.output) { - if (chunk.type === 'chunk' && chunk.name.startsWith(prefix)) { - fs.writeFileSync(`${dir}/${chunk.fileName.slice(prefix.length)}`, chunk.code); - } - } - - for (const remote of manifest_data.remotes) { - fs.unlinkSync(`${dir}/${remote.hash}.tmp.js`); - } -} diff --git a/packages/kit/src/exports/vite/dev/index.js b/packages/kit/src/exports/vite/dev/index.js index e7a27a72e5bb..2d9324e65201 100644 --- a/packages/kit/src/exports/vite/dev/index.js +++ b/packages/kit/src/exports/vite/dev/index.js @@ -29,9 +29,10 @@ const vite_css_query_regex = /(?:\?|&)(?:raw|url|inline)(?:&|$)/; * @param {import('vite').ViteDevServer} vite * @param {import('vite').ResolvedConfig} vite_config * @param {import('types').ValidatedConfig} svelte_config + * @param {() => Array<{ hash: string, file: string }>} get_remotes * @return {Promise void>>} */ -export async function dev(vite, vite_config, svelte_config) { +export async function dev(vite, vite_config, svelte_config, get_remotes) { installPolyfills(); const async_local_storage = new AsyncLocalStorage(); @@ -266,12 +267,14 @@ export async function dev(vite, vite_config, svelte_config) { }; }), prerendered_routes: new Set(), - remotes: Object.fromEntries( - manifest_data.remotes.map((remote) => [ - remote.hash, - () => vite.ssrLoadModule(remote.file) - ]) - ), + get remotes() { + return Object.fromEntries( + get_remotes().map((remote) => [ + remote.hash, + () => vite.ssrLoadModule(remote.file).then((module) => ({ default: module })) + ]) + ); + }, routes: compact( manifest_data.routes.map((route) => { if (!route.page && !route.endpoint) return null; diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 2ad2b626d56a..e3bb6e80907a 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -41,7 +41,6 @@ import { } from './module_ids.js'; import { import_peer } from '../../utils/import.js'; import { compact } from '../../utils/array.js'; -import { build_remotes, treeshake_prerendered_remotes } from './build/build_remote.js'; import { should_ignore } from './static_analysis/utils.js'; const cwd = process.cwd(); @@ -654,10 +653,66 @@ async function kit({ svelte_config }) { /** @type {import('vite').ViteDevServer} */ let dev_server; + /** @type {Array<{ hash: string, file: string }>} */ + const remotes = []; + /** @type {import('vite').Plugin} */ const plugin_remote = { name: 'vite-plugin-sveltekit-remote', + config(config) { + if (!config.build?.ssr) { + // only set manualChunks for the SSR build + return; + } + + // Ensure build.rollupOptions.output exists + config.build ??= {}; + config.build.rollupOptions ??= {}; + config.build.rollupOptions.output ??= {}; + + if (Array.isArray(config.build.rollupOptions.output)) { + // TODO I have no idea how this could occur + throw new Error('rollupOptions.output cannot be an array'); + } + + // Set up manualChunks to isolate *.remote.ts files + const { manualChunks } = config.build.rollupOptions.output; + + config.build.rollupOptions.output = { + ...config.build.rollupOptions.output, + manualChunks(id, meta) { + if (id === `${runtime_directory}/app/server/index.js`) { + return 'app-server'; + } + + // Check if this is a *.remote.ts file + if (svelte_config.kit.moduleExtensions.some((ext) => id.endsWith(`.remote${ext}`))) { + const relative = posixify(path.relative(cwd, id)); + + return `remote-${hash(relative)}`; + } + + // If there was an existing manualChunks function, call it + if (typeof manualChunks === 'function') { + return manualChunks(id, meta); + } + + // If manualChunks is an object, check if this module matches any patterns + if (manualChunks) { + for (const name in manualChunks) { + const patterns = manualChunks[name]; + + // TODO is `id.includes(pattern)` correct? + if (patterns.some((pattern) => id.includes(pattern))) { + return name; + } + } + } + } + }; + }, + configureServer(_dev_server) { dev_server = _dev_server; }, @@ -668,36 +723,40 @@ async function kit({ svelte_config }) { } const file = posixify(path.relative(cwd, id)); - const hashed = hash(file); + + const remote = { + hash: hash(file), + file + }; + + remotes.push(remote); if (opts?.ssr) { - // in dev, add metadata to remote functions by self-importing - if (dev_server) { - return ( - code + - dedent` - import * as $$_self_$$ from './${path.basename(id)}'; - import { validate_remote_functions as $$_validate_$$ } from '@sveltejs/kit/internal'; - - $$_validate_$$($$_self_$$, ${s(file)}); - - for (const [name, fn] of Object.entries($$_self_$$)) { - fn.__.id = ${s(hashed)} + '/' + name; - fn.__.name = name; - } - ` - ); + code += dedent` + import * as $$_self_$$ from './${path.basename(id)}'; + import { init_remote_functions as $$_init_$$ } from '@sveltejs/kit/internal'; + + $$_init_$$($$_self_$$, ${s(file)}, ${s(remote.hash)}); + + for (const [name, fn] of Object.entries($$_self_$$)) { + fn.__.id = ${s(remote.hash)} + '/' + name; + fn.__.name = name; + } + `; + + if (!dev_server) { + // in prod, prevent the functions from being treeshaken. This will + // be replaced with an `export default` in the `writeBundle` hook + code += `$$_export_$$($$_self_$$);`; } - // in prod, return as-is, and augment the build result instead. - // this allows us to treeshake non-dynamic `prerender` functions - return; + return code; } // For the client, read the exports and create a new module that only contains fetch functions with the correct metadata /** @type {Map} */ - const remotes = new Map(); + const map = new Map(); // in dev, load the server module here (which will result in this hook // being called again with `opts.ssr === true` if the module isn't @@ -708,7 +767,7 @@ async function kit({ svelte_config }) { for (const [name, value] of Object.entries(module)) { const type = value?.__?.type; if (type) { - remotes.set(name, type); + map.set(name, type); } } } @@ -716,25 +775,37 @@ async function kit({ svelte_config }) { // in prod, we already built and analysed the server code before // building the client code, so `remote_exports` is populated else if (remote_exports) { - const exports = remote_exports.get(hashed); + const exports = remote_exports.get(remote.hash); if (!exports) throw new Error('Expected to find metadata for remote file ' + id); for (const [name, value] of exports) { - remotes.set(name, value.type); + map.set(name, value.type); } } let namespace = '__remote'; let uid = 1; - while (remotes.has(namespace)) namespace = `__remote${uid++}`; + while (map.has(namespace)) namespace = `__remote${uid++}`; - const exports = Array.from(remotes).map(([name, type]) => { - return `export const ${name} = ${namespace}.${type}('${hashed}/${name}');`; + const exports = Array.from(map).map(([name, type]) => { + return `export const ${name} = ${namespace}.${type}('${remote.hash}/${name}');`; }); return { code: `import * as ${namespace} from '__sveltekit/remote';\n\n${exports.join('\n')}\n` }; + }, + + writeBundle() { + for (const remote of remotes) { + const file = `${out}/server/chunks/remote-${remote.hash}.js`; + const code = fs.readFileSync(file, 'utf-8'); + + fs.writeFileSync( + file, + code.replace('$$_export_$$($$_self_$$)', () => `export default $$_self_$$;`) + ); + } } }; @@ -811,11 +882,6 @@ async function kit({ svelte_config }) { } input['instrumentation.server'] = server_instrumentation; } - - // ...and every .remote file - for (const remote of manifest_data.remotes) { - input[`remote/${remote.hash}`] = path.resolve(remote.file); - } } else if (svelte_config.kit.output.bundleStrategy !== 'split') { input['bundle'] = `${runtime_directory}/client/bundle.js`; } else { @@ -917,7 +983,7 @@ async function kit({ svelte_config }) { * @see https://vitejs.dev/guide/api-plugin.html#configureserver */ async configureServer(vite) { - return await dev(vite, vite_config, svelte_config); + return await dev(vite, vite_config, svelte_config, () => remotes); }, /** @@ -1000,7 +1066,8 @@ async function kit({ svelte_config }) { build_data, prerendered: [], relative_path: '.', - routes: manifest_data.routes + routes: manifest_data.routes, + remotes })};\n` ); @@ -1014,7 +1081,8 @@ async function kit({ svelte_config }) { tracked_features, env: { ...env.private, ...env.public }, out, - output_config: svelte_config.output + output_config: svelte_config.output, + remotes }); remote_exports = metadata.remotes; @@ -1199,7 +1267,8 @@ async function kit({ svelte_config }) { build_data, prerendered: [], relative_path: '.', - routes: manifest_data.routes + routes: manifest_data.routes, + remotes })};\n` ); @@ -1216,9 +1285,6 @@ async function kit({ svelte_config }) { static_exports ); - // ...make sure remote exports have their IDs assigned... - build_remotes(out, manifest_data); - // ...and prerender const { prerendered, prerender_map } = await prerender({ hash: kit.router.type === 'hash', @@ -1236,13 +1302,11 @@ async function kit({ svelte_config }) { build_data, prerendered: prerendered.paths, relative_path: '.', - routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true) + routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true), + remotes })};\n` ); - // remove prerendered remote functions - await treeshake_prerendered_remotes(out, manifest_data, metadata); - if (service_worker_entry_file) { if (kit.paths.assets) { throw new Error('Cannot use service worker alongside config.kit.paths.assets'); @@ -1285,6 +1349,7 @@ async function kit({ svelte_config }) { prerendered, prerender_map, log, + remotes, vite_config ); } else { diff --git a/packages/kit/src/runtime/server/remote.js b/packages/kit/src/runtime/server/remote.js index cbd843af6c72..80896d9d6833 100644 --- a/packages/kit/src/runtime/server/remote.js +++ b/packages/kit/src/runtime/server/remote.js @@ -43,7 +43,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id) if (!remotes[hash]) error(404); const module = await remotes[hash](); - const fn = module[name]; + const fn = module.default[name]; if (!fn) error(404); @@ -202,7 +202,7 @@ async function handle_remote_call_internal(event, state, options, manifest, id) const [hash, name, payload] = key.split('/'); const loader = manifest._.remotes[hash]; - const fn = (await loader?.())?.[name]; + const fn = (await loader?.())?.default?.[name]; if (!fn) error(400, 'Bad Request'); @@ -254,7 +254,7 @@ async function handle_remote_form_post_internal(event, state, manifest, id) { const remotes = manifest._.remotes; const module = await remotes[hash]?.(); - let form = /** @type {RemoteForm} */ (module?.[name]); + let form = /** @type {RemoteForm} */ (module?.default[name]); if (!form) { event.setHeaders({ diff --git a/packages/kit/src/types/internal.d.ts b/packages/kit/src/types/internal.d.ts index 69e78dc46431..216895049c40 100644 --- a/packages/kit/src/types/internal.d.ts +++ b/packages/kit/src/types/internal.d.ts @@ -192,14 +192,15 @@ export interface ManifestData { universal: string | null; }; nodes: PageNode[]; - remotes: Array<{ - file: string; - hash: string; - }>; routes: RouteData[]; matchers: Record; } +export interface RemoteChunk { + hash: string; + file: string; +} + export interface PageNode { depth: number; /** The `+page/layout.svelte`. */ diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts index ddb95c7d6e26..097c03cf9b38 100644 --- a/packages/kit/types/index.d.ts +++ b/packages/kit/types/index.d.ts @@ -2123,10 +2123,6 @@ declare module '@sveltejs/kit' { universal: string | null; }; nodes: PageNode[]; - remotes: Array<{ - file: string; - hash: string; - }>; routes: RouteData[]; matchers: Record; }