From 15fed4764895f330386d6da32b8b70446303b72f Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 7 Dec 2022 21:06:37 -0800 Subject: [PATCH 1/4] [feat] split Vite plugin in two --- .changeset/seven-bikes-give.md | 5 +++++ packages/kit/src/exports/vite/index.js | 15 +++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) create mode 100644 .changeset/seven-bikes-give.md diff --git a/.changeset/seven-bikes-give.md b/.changeset/seven-bikes-give.md new file mode 100644 index 000000000000..2e5276ea30c4 --- /dev/null +++ b/.changeset/seven-bikes-give.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/kit': patch +--- + +[feat] split Vite plugin in two diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 179104d8c19b..7fe786f833d1 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -61,7 +61,7 @@ const enforced_config = { /** @return {import('vite').Plugin[]} */ export function sveltekit() { - return [...svelte(), kit()]; + return [...svelte(), ...kit()]; } /** @@ -74,7 +74,7 @@ export function sveltekit() { * - https://rollupjs.org/guide/en/#build-hooks * - https://rollupjs.org/guide/en/#output-generation-hooks * - * @return {import('vite').Plugin} + * @return {Array} */ function kit() { /** @type {import('types').ValidatedConfig} */ @@ -183,8 +183,8 @@ function kit() { // TODO remove this for 1.0 check_vite_version(); - return { - name: 'vite-plugin-svelte-kit', + return [{ + name: 'vite-plugin-sveltekit-build', /** * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. @@ -539,7 +539,10 @@ function kit() { fs.unlinkSync(`${paths.output_dir}/client/${vite_config.build.manifest}`); fs.unlinkSync(`${paths.output_dir}/server/${vite_config.build.manifest}`); } - }, + } + }, + { + name: 'vite-plugin-sveltekit-middleware', /** * Adds the SvelteKit middleware to do SSR in dev mode. @@ -556,7 +559,7 @@ function kit() { configurePreviewServer(vite) { return preview(vite, vite_config, svelte_config); } - }; + }]; } function check_vite_version() { From 8e52977be344f16d3fc9b75f89d18d6bb8e00c6f Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Wed, 7 Dec 2022 21:14:59 -0800 Subject: [PATCH 2/4] format --- packages/kit/src/exports/vite/index.js | 648 +++++++++++++------------ 1 file changed, 326 insertions(+), 322 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 7fe786f833d1..8eb471b44bdc 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -183,383 +183,387 @@ function kit() { // TODO remove this for 1.0 check_vite_version(); - return [{ - name: 'vite-plugin-sveltekit-build', + return [ + { + name: 'vite-plugin-sveltekit-build', - /** - * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. - * @see https://vitejs.dev/guide/api-plugin.html#config - */ - async config(config, config_env) { - vite_config_env = config_env; - svelte_config = await load_config(); + /** + * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. + * @see https://vitejs.dev/guide/api-plugin.html#config + */ + async config(config, config_env) { + vite_config_env = config_env; + svelte_config = await load_config(); - env = get_env(svelte_config.kit.env, vite_config_env.mode); + env = get_env(svelte_config.kit.env, vite_config_env.mode); - // The config is created in build_server for SSR mode and passed inline - if (config.build?.ssr) return; + // The config is created in build_server for SSR mode and passed inline + if (config.build?.ssr) return; - is_build = config_env.command === 'build'; + is_build = config_env.command === 'build'; - paths = { - build_dir: `${svelte_config.kit.outDir}/build`, - output_dir: `${svelte_config.kit.outDir}/output`, - client_out_dir: `${svelte_config.kit.outDir}/output/client` - }; + paths = { + build_dir: `${svelte_config.kit.outDir}/build`, + output_dir: `${svelte_config.kit.outDir}/output`, + client_out_dir: `${svelte_config.kit.outDir}/output/client` + }; - if (is_build) { - manifest_data = (await sync.all(svelte_config, config_env.mode)).manifest_data; + if (is_build) { + manifest_data = (await sync.all(svelte_config, config_env.mode)).manifest_data; - const new_config = vite_client_build_config(); + const new_config = vite_client_build_config(); - const warning = warn_overridden_config(config, new_config); - if (warning) console.error(warning + '\n'); + const warning = warn_overridden_config(config, new_config); + if (warning) console.error(warning + '\n'); - return new_config; - } + return new_config; + } - const allow = new Set([ - svelte_config.kit.files.lib, - svelte_config.kit.files.routes, - svelte_config.kit.outDir, - path.resolve(cwd, 'src'), // TODO this isn't correct if user changed all his files to sth else than src (like in test/options) - path.resolve(cwd, 'node_modules'), - path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules') - ]); - // We can only add directories to the allow list, so we find out - // if there's a client hooks file and pass its directory - const client_hooks = resolve_entry(svelte_config.kit.files.hooks.client); - if (client_hooks) { - allow.add(path.dirname(client_hooks)); - } + const allow = new Set([ + svelte_config.kit.files.lib, + svelte_config.kit.files.routes, + svelte_config.kit.outDir, + path.resolve(cwd, 'src'), // TODO this isn't correct if user changed all his files to sth else than src (like in test/options) + path.resolve(cwd, 'node_modules'), + path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules') + ]); + // We can only add directories to the allow list, so we find out + // if there's a client hooks file and pass its directory + const client_hooks = resolve_entry(svelte_config.kit.files.hooks.client); + if (client_hooks) { + allow.add(path.dirname(client_hooks)); + } - // dev and preview config can be shared - /** @type {import('vite').UserConfig} */ - const result = { - appType: 'custom', - base: './', - build: { - rollupOptions: { - // Vite dependency crawler needs an explicit JS entry point - // eventhough server otherwise works without it - input: `${runtime_directory}/client/start.js` - } - }, - define: { - __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0', - __SVELTEKIT_BROWSER__: config_env.ssrBuild ? 'false' : 'true', - __SVELTEKIT_DEV__: 'true' - }, - publicDir: svelte_config.kit.files.assets, - resolve: { - alias: [...get_app_aliases(svelte_config.kit), ...get_config_aliases(svelte_config.kit)] - }, - root: cwd, - server: { - fs: { - allow: [...allow] + // dev and preview config can be shared + /** @type {import('vite').UserConfig} */ + const result = { + appType: 'custom', + base: './', + build: { + rollupOptions: { + // Vite dependency crawler needs an explicit JS entry point + // eventhough server otherwise works without it + input: `${runtime_directory}/client/start.js` + } + }, + define: { + __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0', + __SVELTEKIT_BROWSER__: config_env.ssrBuild ? 'false' : 'true', + __SVELTEKIT_DEV__: 'true' }, - watch: { - ignored: [ - // Ignore all siblings of config.kit.outDir/generated - `${posixify(svelte_config.kit.outDir)}/!(generated)` + publicDir: svelte_config.kit.files.assets, + resolve: { + alias: [...get_app_aliases(svelte_config.kit), ...get_config_aliases(svelte_config.kit)] + }, + root: cwd, + server: { + fs: { + allow: [...allow] + }, + watch: { + ignored: [ + // Ignore all siblings of config.kit.outDir/generated + `${posixify(svelte_config.kit.outDir)}/!(generated)` + ] + } + }, + ssr: { + // Without this, Vite will treat `@sveltejs/kit` as noExternal if it's + // a linked dependency, and that causes modules to be imported twice + // under different IDs, which breaks a bunch of stuff + // https://github.com/vitejs/vite/pull/9296 + external: ['@sveltejs/kit'] + }, + optimizeDeps: { + exclude: [ + '@sveltejs/kit', + // exclude kit features so that libraries using them work even when they are prebundled + // this does not affect app code, just handling of imported libraries that use $app or $env + '$app', + '$env' ] } - }, - ssr: { - // Without this, Vite will treat `@sveltejs/kit` as noExternal if it's - // a linked dependency, and that causes modules to be imported twice - // under different IDs, which breaks a bunch of stuff - // https://github.com/vitejs/vite/pull/9296 - external: ['@sveltejs/kit'] - }, - optimizeDeps: { - exclude: [ - '@sveltejs/kit', - // exclude kit features so that libraries using them work even when they are prebundled - // this does not affect app code, just handling of imported libraries that use $app or $env - '$app', - '$env' - ] - } - }; + }; - const warning = warn_overridden_config(config, result); - if (warning) console.error(warning); + const warning = warn_overridden_config(config, result); + if (warning) console.error(warning); - return result; - }, + return result; + }, - async resolveId(id) { - // treat $env/static/[public|private] as virtual - if (id.startsWith('$env/') || id === '$service-worker') return `\0${id}`; - }, + async resolveId(id) { + // treat $env/static/[public|private] as virtual + if (id.startsWith('$env/') || id === '$service-worker') return `\0${id}`; + }, - async load(id, options) { - if (options?.ssr === false) { - const normalized_cwd = vite.normalizePath(cwd); - const normalized_lib = vite.normalizePath(svelte_config.kit.files.lib); - if ( - is_illegal(id, { - cwd: normalized_cwd, - node_modules: vite.normalizePath(path.join(cwd, 'node_modules')), - server: vite.normalizePath(path.join(normalized_lib, 'server')) - }) - ) { - const relative = normalize_id(id, normalized_lib, normalized_cwd); - throw new Error(`Cannot import ${relative} into client-side code`); + async load(id, options) { + if (options?.ssr === false) { + const normalized_cwd = vite.normalizePath(cwd); + const normalized_lib = vite.normalizePath(svelte_config.kit.files.lib); + if ( + is_illegal(id, { + cwd: normalized_cwd, + node_modules: vite.normalizePath(path.join(cwd, 'node_modules')), + server: vite.normalizePath(path.join(normalized_lib, 'server')) + }) + ) { + const relative = normalize_id(id, normalized_lib, normalized_cwd); + throw new Error(`Cannot import ${relative} into client-side code`); + } } - } - - switch (id) { - case '\0$env/static/private': - return create_static_module('$env/static/private', env.private); - case '\0$env/static/public': - return create_static_module('$env/static/public', env.public); - case '\0$env/dynamic/private': - return create_dynamic_module( - 'private', - vite_config_env.command === 'serve' ? env.private : undefined - ); - case '\0$env/dynamic/public': - return create_dynamic_module( - 'public', - vite_config_env.command === 'serve' ? env.public : undefined - ); - case '\0$service-worker': - return create_service_worker_module(svelte_config); - } - }, - - /** - * Stores the final config. - */ - configResolved(config) { - vite_config = config; - - // This is a hack to prevent Vite from nuking useful logs, - // pending https://github.com/vitejs/vite/issues/9378 - config.logger.warn(''); - }, - /** - * Clears the output directories. - */ - buildStart() { - if (vite_config.build.ssr) { - return; - } + switch (id) { + case '\0$env/static/private': + return create_static_module('$env/static/private', env.private); + case '\0$env/static/public': + return create_static_module('$env/static/public', env.public); + case '\0$env/dynamic/private': + return create_dynamic_module( + 'private', + vite_config_env.command === 'serve' ? env.private : undefined + ); + case '\0$env/dynamic/public': + return create_dynamic_module( + 'public', + vite_config_env.command === 'serve' ? env.public : undefined + ); + case '\0$service-worker': + return create_service_worker_module(svelte_config); + } + }, - // Reset for new build. Goes here because `build --watch` calls buildStart but not config - completed_build = false; + /** + * Stores the final config. + */ + configResolved(config) { + vite_config = config; - if (is_build) { - if (!vite_config.build.watch) { - rimraf(paths.build_dir); - rimraf(paths.output_dir); - } - mkdirp(paths.build_dir); - mkdirp(paths.output_dir); - } - }, + // This is a hack to prevent Vite from nuking useful logs, + // pending https://github.com/vitejs/vite/issues/9378 + config.logger.warn(''); + }, - /** - * Vite builds a single bundle. We need three bundles: client, server, and service worker. - * The user's package.json scripts will invoke the Vite CLI to execute the client build. We - * then use this hook to kick off builds for the server and service worker. - */ - writeBundle: { - sequential: true, - async handler(_options, bundle) { + /** + * Clears the output directories. + */ + buildStart() { if (vite_config.build.ssr) { return; } - const guard = module_guard(this, { - cwd: vite.normalizePath(process.cwd()), - lib: vite.normalizePath(svelte_config.kit.files.lib) - }); + // Reset for new build. Goes here because `build --watch` calls buildStart but not config + completed_build = false; - manifest_data.nodes.forEach((_node, i) => { - const id = vite.normalizePath( - path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) - ); + if (is_build) { + if (!vite_config.build.watch) { + rimraf(paths.build_dir); + rimraf(paths.output_dir); + } + mkdirp(paths.build_dir); + mkdirp(paths.output_dir); + } + }, - guard.check(id); - }); + /** + * Vite builds a single bundle. We need three bundles: client, server, and service worker. + * The user's package.json scripts will invoke the Vite CLI to execute the client build. We + * then use this hook to kick off builds for the server and service worker. + */ + writeBundle: { + sequential: true, + async handler(_options, bundle) { + if (vite_config.build.ssr) { + return; + } - const verbose = vite_config.logLevel === 'info'; - log = logger({ - verbose - }); + const guard = module_guard(this, { + cwd: vite.normalizePath(process.cwd()), + lib: vite.normalizePath(svelte_config.kit.files.lib) + }); - fs.writeFileSync( - `${paths.client_out_dir}/${svelte_config.kit.appDir}/version.json`, - JSON.stringify({ version: svelte_config.kit.version.name }) - ); + manifest_data.nodes.forEach((_node, i) => { + const id = vite.normalizePath( + path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) + ); - const { assets, chunks } = collect_output(bundle); - log.info( - `Client build completed. Wrote ${chunks.length} chunks and ${assets.length} assets` - ); + guard.check(id); + }); - log.info('Building server'); - - const options = { - cwd, - config: svelte_config, - vite_config, - vite_config_env, - build_dir: paths.build_dir, // TODO just pass `paths` - manifest_data, - output_dir: paths.output_dir, - service_worker_entry_file: resolve_entry(svelte_config.kit.files.serviceWorker) - }; - const client = client_build_info(assets, chunks); - const server = await build_server(options, client); - - /** @type {import('types').BuildData} */ - build_data = { - app_dir: svelte_config.kit.appDir, - app_path: `${svelte_config.kit.paths.base.slice(1)}${ - svelte_config.kit.paths.base ? '/' : '' - }${svelte_config.kit.appDir}`, - manifest_data, - service_worker: options.service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable? - client, - server - }; + const verbose = vite_config.logLevel === 'info'; + log = logger({ + verbose + }); - const manifest_path = `${paths.output_dir}/server/manifest-full.js`; - fs.writeFileSync( - manifest_path, - `export const manifest = ${generate_manifest({ - build_data, - relative_path: '.', - routes: manifest_data.routes - })};\n` - ); + fs.writeFileSync( + `${paths.client_out_dir}/${svelte_config.kit.appDir}/version.json`, + JSON.stringify({ version: svelte_config.kit.version.name }) + ); - log.info('Prerendering'); - await new Promise((fulfil, reject) => { - const results_path = `${svelte_config.kit.outDir}/generated/prerendered.json`; + const { assets, chunks } = collect_output(bundle); + log.info( + `Client build completed. Wrote ${chunks.length} chunks and ${assets.length} assets` + ); - // do prerendering in a subprocess so any dangling stuff gets killed upon completion - const script = fileURLToPath( - new URL('../../core/prerender/prerender.js', import.meta.url) + log.info('Building server'); + + const options = { + cwd, + config: svelte_config, + vite_config, + vite_config_env, + build_dir: paths.build_dir, // TODO just pass `paths` + manifest_data, + output_dir: paths.output_dir, + service_worker_entry_file: resolve_entry(svelte_config.kit.files.serviceWorker) + }; + const client = client_build_info(assets, chunks); + const server = await build_server(options, client); + + /** @type {import('types').BuildData} */ + build_data = { + app_dir: svelte_config.kit.appDir, + app_path: `${svelte_config.kit.paths.base.slice(1)}${ + svelte_config.kit.paths.base ? '/' : '' + }${svelte_config.kit.appDir}`, + manifest_data, + service_worker: options.service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable? + client, + server + }; + + const manifest_path = `${paths.output_dir}/server/manifest-full.js`; + fs.writeFileSync( + manifest_path, + `export const manifest = ${generate_manifest({ + build_data, + relative_path: '.', + routes: manifest_data.routes + })};\n` ); - const child = fork( - script, - [ - vite_config.build.outDir, - manifest_path, - results_path, - '' + verbose, - JSON.stringify({ ...env.private, ...env.public }) - ], - { - stdio: 'inherit' - } + log.info('Prerendering'); + await new Promise((fulfil, reject) => { + const results_path = `${svelte_config.kit.outDir}/generated/prerendered.json`; + + // do prerendering in a subprocess so any dangling stuff gets killed upon completion + const script = fileURLToPath( + new URL('../../core/prerender/prerender.js', import.meta.url) + ); + + const child = fork( + script, + [ + vite_config.build.outDir, + manifest_path, + results_path, + '' + verbose, + JSON.stringify({ ...env.private, ...env.public }) + ], + { + stdio: 'inherit' + } + ); + + child.on('exit', (code) => { + if (code) { + reject(new Error(`Prerendering failed with code ${code}`)); + } else { + const results = JSON.parse(fs.readFileSync(results_path, 'utf8'), (key, value) => { + if (key === 'pages' || key === 'assets' || key === 'redirects') { + return new Map(value); + } + return value; + }); + + prerendered = results.prerendered; + prerender_map = new Map(results.prerender_map); + + fulfil(undefined); + } + }); + }); + + // generate a new manifest that doesn't include prerendered pages + fs.writeFileSync( + `${paths.output_dir}/server/manifest.js`, + `export const manifest = ${generate_manifest({ + build_data, + relative_path: '.', + routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true) + })};\n` ); - child.on('exit', (code) => { - if (code) { - reject(new Error(`Prerendering failed with code ${code}`)); - } else { - const results = JSON.parse(fs.readFileSync(results_path, 'utf8'), (key, value) => { - if (key === 'pages' || key === 'assets' || key === 'redirects') { - return new Map(value); - } - return value; - }); - - prerendered = results.prerendered; - prerender_map = new Map(results.prerender_map); - - fulfil(undefined); + if (options.service_worker_entry_file) { + if (svelte_config.kit.paths.assets) { + throw new Error('Cannot use service worker alongside config.kit.paths.assets'); } - }); - }); - - // generate a new manifest that doesn't include prerendered pages - fs.writeFileSync( - `${paths.output_dir}/server/manifest.js`, - `export const manifest = ${generate_manifest({ - build_data, - relative_path: '.', - routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true) - })};\n` - ); - if (options.service_worker_entry_file) { - if (svelte_config.kit.paths.assets) { - throw new Error('Cannot use service worker alongside config.kit.paths.assets'); + log.info('Building service worker'); + + await build_service_worker(options, prerendered, client.vite_manifest); } - log.info('Building service worker'); + console.log( + `\nRun ${colors + .bold() + .cyan('npm run preview')} to preview your production build locally.` + ); - await build_service_worker(options, prerendered, client.vite_manifest); + completed_build = true; } + }, - console.log( - `\nRun ${colors.bold().cyan('npm run preview')} to preview your production build locally.` - ); - - completed_build = true; - } - }, + /** + * Runs the adapter. + */ + closeBundle: { + sequential: true, + async handler() { + // vite calls closeBundle when dev-server restarts, ignore that, + // and only adapt when build successfully completes. + const is_restart = !completed_build; + if (vite_config.build.ssr || is_restart) { + return; + } - /** - * Runs the adapter. - */ - closeBundle: { - sequential: true, - async handler() { - // vite calls closeBundle when dev-server restarts, ignore that, - // and only adapt when build successfully completes. - const is_restart = !completed_build; - if (vite_config.build.ssr || is_restart) { - return; - } + if (svelte_config.kit.adapter) { + const { adapt } = await import('../../core/adapt/index.js'); + await adapt(svelte_config, build_data, prerendered, prerender_map, { log }); + } else { + console.log(colors.bold().yellow('\nNo adapter specified')); - if (svelte_config.kit.adapter) { - const { adapt } = await import('../../core/adapt/index.js'); - await adapt(svelte_config, build_data, prerendered, prerender_map, { log }); - } else { - console.log(colors.bold().yellow('\nNo adapter specified')); + const link = colors.bold().cyan('https://kit.svelte.dev/docs/adapters'); + console.log( + `See ${link} to learn how to configure your app to run on the platform of your choosing` + ); + } - const link = colors.bold().cyan('https://kit.svelte.dev/docs/adapters'); - console.log( - `See ${link} to learn how to configure your app to run on the platform of your choosing` - ); + // avoid making the manifest available to users + fs.unlinkSync(`${paths.output_dir}/client/${vite_config.build.manifest}`); + fs.unlinkSync(`${paths.output_dir}/server/${vite_config.build.manifest}`); } - - // avoid making the manifest available to users - fs.unlinkSync(`${paths.output_dir}/client/${vite_config.build.manifest}`); - fs.unlinkSync(`${paths.output_dir}/server/${vite_config.build.manifest}`); } - } - }, - { - name: 'vite-plugin-sveltekit-middleware', - - /** - * Adds the SvelteKit middleware to do SSR in dev mode. - * @see https://vitejs.dev/guide/api-plugin.html#configureserver - */ - async configureServer(vite) { - return await dev(vite, vite_config, svelte_config); }, + { + name: 'vite-plugin-sveltekit-middleware', + + /** + * Adds the SvelteKit middleware to do SSR in dev mode. + * @see https://vitejs.dev/guide/api-plugin.html#configureserver + */ + async configureServer(vite) { + return await dev(vite, vite_config, svelte_config); + }, - /** - * Adds the SvelteKit middleware to do SSR in preview mode. - * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver - */ - configurePreviewServer(vite) { - return preview(vite, vite_config, svelte_config); + /** + * Adds the SvelteKit middleware to do SSR in preview mode. + * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver + */ + configurePreviewServer(vite) { + return preview(vite, vite_config, svelte_config); + } } - }]; + ]; } function check_vite_version() { From 04f484882a4cd69f7106638ad535e6e824a9221b Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 8 Dec 2022 06:25:46 -0800 Subject: [PATCH 3/4] avoid extra level of indentation --- packages/kit/src/exports/vite/index.js | 653 +++++++++++++------------ 1 file changed, 327 insertions(+), 326 deletions(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 8eb471b44bdc..19ab9cc25cde 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -183,387 +183,388 @@ function kit() { // TODO remove this for 1.0 check_vite_version(); - return [ - { - name: 'vite-plugin-sveltekit-build', + /** @type {import('vite').Plugin} */ + const plugin_build = { + name: 'vite-plugin-sveltekit-build', - /** - * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. - * @see https://vitejs.dev/guide/api-plugin.html#config - */ - async config(config, config_env) { - vite_config_env = config_env; - svelte_config = await load_config(); + /** + * Build the SvelteKit-provided Vite config to be merged with the user's vite.config.js file. + * @see https://vitejs.dev/guide/api-plugin.html#config + */ + async config(config, config_env) { + vite_config_env = config_env; + svelte_config = await load_config(); - env = get_env(svelte_config.kit.env, vite_config_env.mode); + env = get_env(svelte_config.kit.env, vite_config_env.mode); - // The config is created in build_server for SSR mode and passed inline - if (config.build?.ssr) return; + // The config is created in build_server for SSR mode and passed inline + if (config.build?.ssr) return; - is_build = config_env.command === 'build'; + is_build = config_env.command === 'build'; - paths = { - build_dir: `${svelte_config.kit.outDir}/build`, - output_dir: `${svelte_config.kit.outDir}/output`, - client_out_dir: `${svelte_config.kit.outDir}/output/client` - }; + paths = { + build_dir: `${svelte_config.kit.outDir}/build`, + output_dir: `${svelte_config.kit.outDir}/output`, + client_out_dir: `${svelte_config.kit.outDir}/output/client` + }; - if (is_build) { - manifest_data = (await sync.all(svelte_config, config_env.mode)).manifest_data; + if (is_build) { + manifest_data = (await sync.all(svelte_config, config_env.mode)).manifest_data; - const new_config = vite_client_build_config(); + const new_config = vite_client_build_config(); - const warning = warn_overridden_config(config, new_config); - if (warning) console.error(warning + '\n'); + const warning = warn_overridden_config(config, new_config); + if (warning) console.error(warning + '\n'); - return new_config; - } + return new_config; + } - const allow = new Set([ - svelte_config.kit.files.lib, - svelte_config.kit.files.routes, - svelte_config.kit.outDir, - path.resolve(cwd, 'src'), // TODO this isn't correct if user changed all his files to sth else than src (like in test/options) - path.resolve(cwd, 'node_modules'), - path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules') - ]); - // We can only add directories to the allow list, so we find out - // if there's a client hooks file and pass its directory - const client_hooks = resolve_entry(svelte_config.kit.files.hooks.client); - if (client_hooks) { - allow.add(path.dirname(client_hooks)); - } + const allow = new Set([ + svelte_config.kit.files.lib, + svelte_config.kit.files.routes, + svelte_config.kit.outDir, + path.resolve(cwd, 'src'), // TODO this isn't correct if user changed all his files to sth else than src (like in test/options) + path.resolve(cwd, 'node_modules'), + path.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules') + ]); + // We can only add directories to the allow list, so we find out + // if there's a client hooks file and pass its directory + const client_hooks = resolve_entry(svelte_config.kit.files.hooks.client); + if (client_hooks) { + allow.add(path.dirname(client_hooks)); + } - // dev and preview config can be shared - /** @type {import('vite').UserConfig} */ - const result = { - appType: 'custom', - base: './', - build: { - rollupOptions: { - // Vite dependency crawler needs an explicit JS entry point - // eventhough server otherwise works without it - input: `${runtime_directory}/client/start.js` - } - }, - define: { - __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0', - __SVELTEKIT_BROWSER__: config_env.ssrBuild ? 'false' : 'true', - __SVELTEKIT_DEV__: 'true' - }, - publicDir: svelte_config.kit.files.assets, - resolve: { - alias: [...get_app_aliases(svelte_config.kit), ...get_config_aliases(svelte_config.kit)] - }, - root: cwd, - server: { - fs: { - allow: [...allow] - }, - watch: { - ignored: [ - // Ignore all siblings of config.kit.outDir/generated - `${posixify(svelte_config.kit.outDir)}/!(generated)` - ] - } - }, - ssr: { - // Without this, Vite will treat `@sveltejs/kit` as noExternal if it's - // a linked dependency, and that causes modules to be imported twice - // under different IDs, which breaks a bunch of stuff - // https://github.com/vitejs/vite/pull/9296 - external: ['@sveltejs/kit'] + // dev and preview config can be shared + /** @type {import('vite').UserConfig} */ + const result = { + appType: 'custom', + base: './', + build: { + rollupOptions: { + // Vite dependency crawler needs an explicit JS entry point + // eventhough server otherwise works without it + input: `${runtime_directory}/client/start.js` + } + }, + define: { + __SVELTEKIT_APP_VERSION_POLL_INTERVAL__: '0', + __SVELTEKIT_BROWSER__: config_env.ssrBuild ? 'false' : 'true', + __SVELTEKIT_DEV__: 'true' + }, + publicDir: svelte_config.kit.files.assets, + resolve: { + alias: [...get_app_aliases(svelte_config.kit), ...get_config_aliases(svelte_config.kit)] + }, + root: cwd, + server: { + fs: { + allow: [...allow] }, - optimizeDeps: { - exclude: [ - '@sveltejs/kit', - // exclude kit features so that libraries using them work even when they are prebundled - // this does not affect app code, just handling of imported libraries that use $app or $env - '$app', - '$env' + watch: { + ignored: [ + // Ignore all siblings of config.kit.outDir/generated + `${posixify(svelte_config.kit.outDir)}/!(generated)` ] } - }; + }, + ssr: { + // Without this, Vite will treat `@sveltejs/kit` as noExternal if it's + // a linked dependency, and that causes modules to be imported twice + // under different IDs, which breaks a bunch of stuff + // https://github.com/vitejs/vite/pull/9296 + external: ['@sveltejs/kit'] + }, + optimizeDeps: { + exclude: [ + '@sveltejs/kit', + // exclude kit features so that libraries using them work even when they are prebundled + // this does not affect app code, just handling of imported libraries that use $app or $env + '$app', + '$env' + ] + } + }; - const warning = warn_overridden_config(config, result); - if (warning) console.error(warning); + const warning = warn_overridden_config(config, result); + if (warning) console.error(warning); - return result; - }, + return result; + }, - async resolveId(id) { - // treat $env/static/[public|private] as virtual - if (id.startsWith('$env/') || id === '$service-worker') return `\0${id}`; - }, + async resolveId(id) { + // treat $env/static/[public|private] as virtual + if (id.startsWith('$env/') || id === '$service-worker') return `\0${id}`; + }, - async load(id, options) { - if (options?.ssr === false) { - const normalized_cwd = vite.normalizePath(cwd); - const normalized_lib = vite.normalizePath(svelte_config.kit.files.lib); - if ( - is_illegal(id, { - cwd: normalized_cwd, - node_modules: vite.normalizePath(path.join(cwd, 'node_modules')), - server: vite.normalizePath(path.join(normalized_lib, 'server')) - }) - ) { - const relative = normalize_id(id, normalized_lib, normalized_cwd); - throw new Error(`Cannot import ${relative} into client-side code`); - } + async load(id, options) { + if (options?.ssr === false) { + const normalized_cwd = vite.normalizePath(cwd); + const normalized_lib = vite.normalizePath(svelte_config.kit.files.lib); + if ( + is_illegal(id, { + cwd: normalized_cwd, + node_modules: vite.normalizePath(path.join(cwd, 'node_modules')), + server: vite.normalizePath(path.join(normalized_lib, 'server')) + }) + ) { + const relative = normalize_id(id, normalized_lib, normalized_cwd); + throw new Error(`Cannot import ${relative} into client-side code`); } + } - switch (id) { - case '\0$env/static/private': - return create_static_module('$env/static/private', env.private); - case '\0$env/static/public': - return create_static_module('$env/static/public', env.public); - case '\0$env/dynamic/private': - return create_dynamic_module( - 'private', - vite_config_env.command === 'serve' ? env.private : undefined - ); - case '\0$env/dynamic/public': - return create_dynamic_module( - 'public', - vite_config_env.command === 'serve' ? env.public : undefined - ); - case '\0$service-worker': - return create_service_worker_module(svelte_config); - } - }, + switch (id) { + case '\0$env/static/private': + return create_static_module('$env/static/private', env.private); + case '\0$env/static/public': + return create_static_module('$env/static/public', env.public); + case '\0$env/dynamic/private': + return create_dynamic_module( + 'private', + vite_config_env.command === 'serve' ? env.private : undefined + ); + case '\0$env/dynamic/public': + return create_dynamic_module( + 'public', + vite_config_env.command === 'serve' ? env.public : undefined + ); + case '\0$service-worker': + return create_service_worker_module(svelte_config); + } + }, - /** - * Stores the final config. - */ - configResolved(config) { - vite_config = config; + /** + * Stores the final config. + */ + configResolved(config) { + vite_config = config; - // This is a hack to prevent Vite from nuking useful logs, - // pending https://github.com/vitejs/vite/issues/9378 - config.logger.warn(''); - }, + // This is a hack to prevent Vite from nuking useful logs, + // pending https://github.com/vitejs/vite/issues/9378 + config.logger.warn(''); + }, - /** - * Clears the output directories. - */ - buildStart() { + /** + * Clears the output directories. + */ + buildStart() { + if (vite_config.build.ssr) { + return; + } + + // Reset for new build. Goes here because `build --watch` calls buildStart but not config + completed_build = false; + + if (is_build) { + if (!vite_config.build.watch) { + rimraf(paths.build_dir); + rimraf(paths.output_dir); + } + mkdirp(paths.build_dir); + mkdirp(paths.output_dir); + } + }, + + /** + * Vite builds a single bundle. We need three bundles: client, server, and service worker. + * The user's package.json scripts will invoke the Vite CLI to execute the client build. We + * then use this hook to kick off builds for the server and service worker. + */ + writeBundle: { + sequential: true, + async handler(_options, bundle) { if (vite_config.build.ssr) { return; } - // Reset for new build. Goes here because `build --watch` calls buildStart but not config - completed_build = false; + const guard = module_guard(this, { + cwd: vite.normalizePath(process.cwd()), + lib: vite.normalizePath(svelte_config.kit.files.lib) + }); - if (is_build) { - if (!vite_config.build.watch) { - rimraf(paths.build_dir); - rimraf(paths.output_dir); - } - mkdirp(paths.build_dir); - mkdirp(paths.output_dir); - } - }, + manifest_data.nodes.forEach((_node, i) => { + const id = vite.normalizePath( + path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) + ); - /** - * Vite builds a single bundle. We need three bundles: client, server, and service worker. - * The user's package.json scripts will invoke the Vite CLI to execute the client build. We - * then use this hook to kick off builds for the server and service worker. - */ - writeBundle: { - sequential: true, - async handler(_options, bundle) { - if (vite_config.build.ssr) { - return; - } + guard.check(id); + }); - const guard = module_guard(this, { - cwd: vite.normalizePath(process.cwd()), - lib: vite.normalizePath(svelte_config.kit.files.lib) - }); + const verbose = vite_config.logLevel === 'info'; + log = logger({ + verbose + }); - manifest_data.nodes.forEach((_node, i) => { - const id = vite.normalizePath( - path.resolve(svelte_config.kit.outDir, `generated/nodes/${i}.js`) - ); + fs.writeFileSync( + `${paths.client_out_dir}/${svelte_config.kit.appDir}/version.json`, + JSON.stringify({ version: svelte_config.kit.version.name }) + ); - guard.check(id); - }); + const { assets, chunks } = collect_output(bundle); + log.info( + `Client build completed. Wrote ${chunks.length} chunks and ${assets.length} assets` + ); - const verbose = vite_config.logLevel === 'info'; - log = logger({ - verbose - }); + log.info('Building server'); + + const options = { + cwd, + config: svelte_config, + vite_config, + vite_config_env, + build_dir: paths.build_dir, // TODO just pass `paths` + manifest_data, + output_dir: paths.output_dir, + service_worker_entry_file: resolve_entry(svelte_config.kit.files.serviceWorker) + }; + const client = client_build_info(assets, chunks); + const server = await build_server(options, client); + + /** @type {import('types').BuildData} */ + build_data = { + app_dir: svelte_config.kit.appDir, + app_path: `${svelte_config.kit.paths.base.slice(1)}${ + svelte_config.kit.paths.base ? '/' : '' + }${svelte_config.kit.appDir}`, + manifest_data, + service_worker: options.service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable? + client, + server + }; - fs.writeFileSync( - `${paths.client_out_dir}/${svelte_config.kit.appDir}/version.json`, - JSON.stringify({ version: svelte_config.kit.version.name }) - ); + const manifest_path = `${paths.output_dir}/server/manifest-full.js`; + fs.writeFileSync( + manifest_path, + `export const manifest = ${generate_manifest({ + build_data, + relative_path: '.', + routes: manifest_data.routes + })};\n` + ); - const { assets, chunks } = collect_output(bundle); - log.info( - `Client build completed. Wrote ${chunks.length} chunks and ${assets.length} assets` - ); + log.info('Prerendering'); + await new Promise((fulfil, reject) => { + const results_path = `${svelte_config.kit.outDir}/generated/prerendered.json`; - log.info('Building server'); - - const options = { - cwd, - config: svelte_config, - vite_config, - vite_config_env, - build_dir: paths.build_dir, // TODO just pass `paths` - manifest_data, - output_dir: paths.output_dir, - service_worker_entry_file: resolve_entry(svelte_config.kit.files.serviceWorker) - }; - const client = client_build_info(assets, chunks); - const server = await build_server(options, client); - - /** @type {import('types').BuildData} */ - build_data = { - app_dir: svelte_config.kit.appDir, - app_path: `${svelte_config.kit.paths.base.slice(1)}${ - svelte_config.kit.paths.base ? '/' : '' - }${svelte_config.kit.appDir}`, - manifest_data, - service_worker: options.service_worker_entry_file ? 'service-worker.js' : null, // TODO make file configurable? - client, - server - }; - - const manifest_path = `${paths.output_dir}/server/manifest-full.js`; - fs.writeFileSync( - manifest_path, - `export const manifest = ${generate_manifest({ - build_data, - relative_path: '.', - routes: manifest_data.routes - })};\n` + // do prerendering in a subprocess so any dangling stuff gets killed upon completion + const script = fileURLToPath( + new URL('../../core/prerender/prerender.js', import.meta.url) ); - log.info('Prerendering'); - await new Promise((fulfil, reject) => { - const results_path = `${svelte_config.kit.outDir}/generated/prerendered.json`; - - // do prerendering in a subprocess so any dangling stuff gets killed upon completion - const script = fileURLToPath( - new URL('../../core/prerender/prerender.js', import.meta.url) - ); - - const child = fork( - script, - [ - vite_config.build.outDir, - manifest_path, - results_path, - '' + verbose, - JSON.stringify({ ...env.private, ...env.public }) - ], - { - stdio: 'inherit' - } - ); - - child.on('exit', (code) => { - if (code) { - reject(new Error(`Prerendering failed with code ${code}`)); - } else { - const results = JSON.parse(fs.readFileSync(results_path, 'utf8'), (key, value) => { - if (key === 'pages' || key === 'assets' || key === 'redirects') { - return new Map(value); - } - return value; - }); - - prerendered = results.prerendered; - prerender_map = new Map(results.prerender_map); - - fulfil(undefined); - } - }); - }); - - // generate a new manifest that doesn't include prerendered pages - fs.writeFileSync( - `${paths.output_dir}/server/manifest.js`, - `export const manifest = ${generate_manifest({ - build_data, - relative_path: '.', - routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true) - })};\n` + const child = fork( + script, + [ + vite_config.build.outDir, + manifest_path, + results_path, + '' + verbose, + JSON.stringify({ ...env.private, ...env.public }) + ], + { + stdio: 'inherit' + } ); - if (options.service_worker_entry_file) { - if (svelte_config.kit.paths.assets) { - throw new Error('Cannot use service worker alongside config.kit.paths.assets'); + child.on('exit', (code) => { + if (code) { + reject(new Error(`Prerendering failed with code ${code}`)); + } else { + const results = JSON.parse(fs.readFileSync(results_path, 'utf8'), (key, value) => { + if (key === 'pages' || key === 'assets' || key === 'redirects') { + return new Map(value); + } + return value; + }); + + prerendered = results.prerendered; + prerender_map = new Map(results.prerender_map); + + fulfil(undefined); } + }); + }); + + // generate a new manifest that doesn't include prerendered pages + fs.writeFileSync( + `${paths.output_dir}/server/manifest.js`, + `export const manifest = ${generate_manifest({ + build_data, + relative_path: '.', + routes: manifest_data.routes.filter((route) => prerender_map.get(route.id) !== true) + })};\n` + ); - log.info('Building service worker'); - - await build_service_worker(options, prerendered, client.vite_manifest); + if (options.service_worker_entry_file) { + if (svelte_config.kit.paths.assets) { + throw new Error('Cannot use service worker alongside config.kit.paths.assets'); } - console.log( - `\nRun ${colors - .bold() - .cyan('npm run preview')} to preview your production build locally.` - ); + log.info('Building service worker'); - completed_build = true; + await build_service_worker(options, prerendered, client.vite_manifest); } - }, - /** - * Runs the adapter. - */ - closeBundle: { - sequential: true, - async handler() { - // vite calls closeBundle when dev-server restarts, ignore that, - // and only adapt when build successfully completes. - const is_restart = !completed_build; - if (vite_config.build.ssr || is_restart) { - return; - } + console.log( + `\nRun ${colors.bold().cyan('npm run preview')} to preview your production build locally.` + ); - if (svelte_config.kit.adapter) { - const { adapt } = await import('../../core/adapt/index.js'); - await adapt(svelte_config, build_data, prerendered, prerender_map, { log }); - } else { - console.log(colors.bold().yellow('\nNo adapter specified')); + completed_build = true; + } + }, - const link = colors.bold().cyan('https://kit.svelte.dev/docs/adapters'); - console.log( - `See ${link} to learn how to configure your app to run on the platform of your choosing` - ); - } + /** + * Runs the adapter. + */ + closeBundle: { + sequential: true, + async handler() { + // vite calls closeBundle when dev-server restarts, ignore that, + // and only adapt when build successfully completes. + const is_restart = !completed_build; + if (vite_config.build.ssr || is_restart) { + return; + } + + if (svelte_config.kit.adapter) { + const { adapt } = await import('../../core/adapt/index.js'); + await adapt(svelte_config, build_data, prerendered, prerender_map, { log }); + } else { + console.log(colors.bold().yellow('\nNo adapter specified')); - // avoid making the manifest available to users - fs.unlinkSync(`${paths.output_dir}/client/${vite_config.build.manifest}`); - fs.unlinkSync(`${paths.output_dir}/server/${vite_config.build.manifest}`); + const link = colors.bold().cyan('https://kit.svelte.dev/docs/adapters'); + console.log( + `See ${link} to learn how to configure your app to run on the platform of your choosing` + ); } + + // avoid making the manifest available to users + fs.unlinkSync(`${paths.output_dir}/client/${vite_config.build.manifest}`); + fs.unlinkSync(`${paths.output_dir}/server/${vite_config.build.manifest}`); } + } + }; + + /** @type {import('vite').Plugin} */ + const plugin_middleware = { + name: 'vite-plugin-sveltekit-middleware', + + /** + * Adds the SvelteKit middleware to do SSR in dev mode. + * @see https://vitejs.dev/guide/api-plugin.html#configureserver + */ + async configureServer(vite) { + return await dev(vite, vite_config, svelte_config); }, - { - name: 'vite-plugin-sveltekit-middleware', - - /** - * Adds the SvelteKit middleware to do SSR in dev mode. - * @see https://vitejs.dev/guide/api-plugin.html#configureserver - */ - async configureServer(vite) { - return await dev(vite, vite_config, svelte_config); - }, - /** - * Adds the SvelteKit middleware to do SSR in preview mode. - * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver - */ - configurePreviewServer(vite) { - return preview(vite, vite_config, svelte_config); - } + /** + * Adds the SvelteKit middleware to do SSR in preview mode. + * @see https://vitejs.dev/guide/api-plugin.html#configurepreviewserver + */ + configurePreviewServer(vite) { + return preview(vite, vite_config, svelte_config); } - ]; + }; + + return [plugin_build, plugin_middleware]; } function check_vite_version() { From aedc86bc1fe6693b78e80e8996bd83b16dffb32e Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Thu, 8 Dec 2022 09:49:54 -0800 Subject: [PATCH 4/4] Update packages/kit/src/exports/vite/index.js Co-authored-by: Rich Harris --- packages/kit/src/exports/vite/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/kit/src/exports/vite/index.js b/packages/kit/src/exports/vite/index.js index 19ab9cc25cde..0b6dcbb79e4e 100644 --- a/packages/kit/src/exports/vite/index.js +++ b/packages/kit/src/exports/vite/index.js @@ -74,7 +74,7 @@ export function sveltekit() { * - https://rollupjs.org/guide/en/#build-hooks * - https://rollupjs.org/guide/en/#output-generation-hooks * - * @return {Array} + * @return {import('vite').Plugin[]} */ function kit() { /** @type {import('types').ValidatedConfig} */