diff --git a/.changeset/fresh-pots-smoke.md b/.changeset/fresh-pots-smoke.md
new file mode 100644
index 000000000000..7ebeba8835bf
--- /dev/null
+++ b/.changeset/fresh-pots-smoke.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': major
+---
+
+breaking: write tsconfig to `node_modules/$app/tsconfig`
diff --git a/.changeset/moody-bags-heal.md b/.changeset/moody-bags-heal.md
new file mode 100644
index 000000000000..1b12a3b4509d
--- /dev/null
+++ b/.changeset/moody-bags-heal.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': minor
+---
+
+feat: `$app/service-worker` module
diff --git a/.changeset/plain-jobs-flash.md b/.changeset/plain-jobs-flash.md
new file mode 100644
index 000000000000..600633abee7a
--- /dev/null
+++ b/.changeset/plain-jobs-flash.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': minor
+---
+
+feat: better tsconfig validation
diff --git a/documentation/docs/10-getting-started/30-project-structure.md b/documentation/docs/10-getting-started/30-project-structure.md
index c356e6743415..cdc04118e615 100644
--- a/documentation/docs/10-getting-started/30-project-structure.md
+++ b/documentation/docs/10-getting-started/30-project-structure.md
@@ -13,11 +13,13 @@ my-project/
│ │ └ [your param matchers]
│ ├ routes/
│ │ └ [your routes]
+│ ├ service-worker/
+│ │ ├ index.js
+│ │ └ tsconfig.json
│ ├ app.html
│ ├ error.html
│ ├ hooks.client.js
│ ├ hooks.server.js
-│ ├ service-worker.js
│ └ instrumentation.server.js
├ static/
│ └ [your static assets]
@@ -52,7 +54,7 @@ The `src` directory contains the meat of your project. Everything except `src/ro
- `%sveltekit.error.message%` — the error message
- `hooks.client.js` contains your client [hooks](hooks)
- `hooks.server.js` contains your server [hooks](hooks)
-- `service-worker.js` contains your [service worker](service-workers)
+- `service-worker` contains your [service worker](service-workers)
- `instrumentation.server.js` contains your [observability](observability) setup and instrumentation code
- Requires adapter support. If your adapter supports it, it is guaranteed to run prior to loading and running your application code.
diff --git a/documentation/docs/98-reference/20-$app-service-worker.md b/documentation/docs/98-reference/20-$app-service-worker.md
new file mode 100644
index 000000000000..796d4adbe735
--- /dev/null
+++ b/documentation/docs/98-reference/20-$app-service-worker.md
@@ -0,0 +1,7 @@
+---
+title: $app/service-worker
+---
+
+This module can only be imported in service workers.
+
+> MODULE: $app/service-worker
diff --git a/documentation/docs/98-reference/20-$app-tsconfig-service-worker.md b/documentation/docs/98-reference/20-$app-tsconfig-service-worker.md
new file mode 100644
index 000000000000..1c8fa148dab4
--- /dev/null
+++ b/documentation/docs/98-reference/20-$app-tsconfig-service-worker.md
@@ -0,0 +1,14 @@
+---
+title: $app/tsconfig/service-worker
+---
+
+This module contains TypeScript configuration tailored for your service worker:
+
+```json
+/// file: src/service-worker/tsconfig.json
+{
+ "extends": "$app/tsconfig/service-worker"
+}
+```
+
+You can extend this configuration with your own `compilerOptions`, adhering to the same restrictions as [`$app/tsconfig`]($app-tsconfig).
diff --git a/documentation/docs/98-reference/20-$app-tsconfig.md b/documentation/docs/98-reference/20-$app-tsconfig.md
new file mode 100644
index 000000000000..c46044a8f679
--- /dev/null
+++ b/documentation/docs/98-reference/20-$app-tsconfig.md
@@ -0,0 +1,23 @@
+---
+title: $app/tsconfig
+---
+
+This module contains TypeScript configuration tailored for your app. Your own config should extend it — a typical `tsconfig.json` looks like this:
+
+```json
+/// file: tsconfig.json
+{
+ "extends": "$app/tsconfig",
+ "includes": ["src", "test"],
+ "excludes": ["src/service-worker"]
+}
+```
+
+You can extend this configuration with your own `compilerOptions`. Overriding the following properties may cause things to break — SvelteKit will warn you if this happens:
+
+- `paths` — this is derived from the (deprecated) [`alias`](configuration#alias) config option, together with any [subpath imports](https://nodejs.org/api/packages.html#subpath-imports) specified in your `package.json`, to align behaviour between Vite and TypeScript. Ideally, configure subpath imports rather than using `paths` directly
+- `types` — your app needs to be able to 'see' generated module declarations for things like [environment variables](environment-variables), and as such this array must include `"$app/types"`
+- `isolatedModules` — must be `true`, as Vite compiles modules one at a time
+- `verbatimModuleSyntax` — must be `true`, so that you can safely use type imports in `.svelte` files
+
+Note that the example configuration above excludes `src/service-worker`, because service workers need to be in their own TypeScript project. If you are using a service worker, create a `src/service-worker/tsconfig.json` that extends [`$app/tsconfig/service-worker`]($app-tsconfig-service-worker).
diff --git a/documentation/docs/98-reference/54-types.md b/documentation/docs/98-reference/54-types.md
index d1d311ff036b..303609691982 100644
--- a/documentation/docs/98-reference/54-types.md
+++ b/documentation/docs/98-reference/54-types.md
@@ -119,80 +119,9 @@ Starting with version 2.16.0, two additional helper types are provided: `PagePro
>
> ```
-> [!NOTE] For this to work, your own `tsconfig.json` or `jsconfig.json` should extend from the generated `.svelte-kit/tsconfig.json` (where `.svelte-kit` is your [`outDir`](configuration#outDir)):
+> [!NOTE] For this to work, your own `tsconfig.json` or `jsconfig.json` should extend from the generated `$app/types`:
>
-> `{ "extends": "./.svelte-kit/tsconfig.json" }`
-
-### Default tsconfig.json
-
-The generated `.svelte-kit/tsconfig.json` file contains a mixture of options. Some are generated programmatically based on your project configuration, and should generally not be overridden without good reason:
-
-```json
-/// file: .svelte-kit/tsconfig.json
-{
- "compilerOptions": {
- "paths": {
- "#lib": ["../src/lib/index.js"],
- "#lib/*": ["../src/lib/*"]
- },
- "rootDirs": ["..", "./types"]
- },
- "include": [
- "ambient.d.ts",
- "non-ambient.d.ts",
- "./types/**/$types.d.ts",
- "../vite.config.js",
- "../vite.config.ts",
- "../src/**/*.js",
- "../src/**/*.ts",
- "../src/**/*.svelte",
- "../tests/**/*.js",
- "../tests/**/*.ts",
- "../tests/**/*.svelte"
- ],
- "exclude": [
- "../node_modules/**",
- "../src/service-worker.js",
- "../src/service-worker/**/*.js",
- "../src/service-worker.ts",
- "../src/service-worker/**/*.ts",
- "../src/service-worker.d.ts",
- "../src/service-worker/**/*.d.ts"
- ]
-}
-```
-
-Others are required for SvelteKit to work properly, and should also be left untouched unless you know what you're doing:
-
-```json
-/// file: .svelte-kit/tsconfig.json
-{
- "compilerOptions": {
- // this ensures that types are explicitly
- // imported with `import type`, which is
- // necessary as Svelte/Vite cannot
- // otherwise compile components correctly
- "verbatimModuleSyntax": true,
-
- // Vite compiles one TypeScript module
- // at a time, rather than compiling
- // the entire module graph
- "isolatedModules": true,
-
- // Tell TS it's used only for type-checking
- "noEmit": true,
-
- // This ensures both `vite build`
- // and `svelte-package` work correctly
- "lib": ["esnext", "DOM", "DOM.Iterable"],
- "moduleResolution": "bundler",
- "module": "esnext",
- "target": "esnext"
- }
-}
-```
-
-Use the [`typescript.config` setting](configuration#typescript) of the SvelteKit plugin in `vite.config.js` to extend or modify the generated `tsconfig.json`.
+> `{ "extends": "$app/tsconfig" }`
## app.d.ts
diff --git a/eslint.config.js b/eslint.config.js
index 2adc2878bd36..66280d53e7c7 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -45,7 +45,9 @@ export default [
{
languageOptions: {
parserOptions: {
- projectService: true
+ projectService: {
+ allowDefaultProject: ['packages/kit/src/runtime/app/service-worker/index.js']
+ }
}
},
rules: {
diff --git a/packages/adapter-cloudflare/test/apps/pages/tsconfig.json b/packages/adapter-cloudflare/test/apps/pages/tsconfig.json
index c2db702f9bb1..0c2092500732 100644
--- a/packages/adapter-cloudflare/test/apps/pages/tsconfig.json
+++ b/packages/adapter-cloudflare/test/apps/pages/tsconfig.json
@@ -9,5 +9,5 @@
"sourceMap": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-cloudflare/test/apps/workers/tsconfig.json b/packages/adapter-cloudflare/test/apps/workers/tsconfig.json
index c2db702f9bb1..0c2092500732 100644
--- a/packages/adapter-cloudflare/test/apps/workers/tsconfig.json
+++ b/packages/adapter-cloudflare/test/apps/workers/tsconfig.json
@@ -9,5 +9,5 @@
"sourceMap": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-netlify/test/apps/basic/tsconfig.json b/packages/adapter-netlify/test/apps/basic/tsconfig.json
index c2db702f9bb1..0c2092500732 100644
--- a/packages/adapter-netlify/test/apps/basic/tsconfig.json
+++ b/packages/adapter-netlify/test/apps/basic/tsconfig.json
@@ -9,5 +9,5 @@
"sourceMap": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-netlify/test/apps/edge/tsconfig.json b/packages/adapter-netlify/test/apps/edge/tsconfig.json
index c2db702f9bb1..0c2092500732 100644
--- a/packages/adapter-netlify/test/apps/edge/tsconfig.json
+++ b/packages/adapter-netlify/test/apps/edge/tsconfig.json
@@ -9,5 +9,5 @@
"sourceMap": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-netlify/test/apps/instrumentation/tsconfig.json b/packages/adapter-netlify/test/apps/instrumentation/tsconfig.json
index 34380ebc986e..12550521f550 100644
--- a/packages/adapter-netlify/test/apps/instrumentation/tsconfig.json
+++ b/packages/adapter-netlify/test/apps/instrumentation/tsconfig.json
@@ -10,5 +10,5 @@
"strict": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-netlify/test/apps/split/tsconfig.json b/packages/adapter-netlify/test/apps/split/tsconfig.json
index c2db702f9bb1..0c2092500732 100644
--- a/packages/adapter-netlify/test/apps/split/tsconfig.json
+++ b/packages/adapter-netlify/test/apps/split/tsconfig.json
@@ -9,5 +9,5 @@
"sourceMap": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-node/test/apps/basic/tsconfig.json b/packages/adapter-node/test/apps/basic/tsconfig.json
index c2db702f9bb1..0c2092500732 100644
--- a/packages/adapter-node/test/apps/basic/tsconfig.json
+++ b/packages/adapter-node/test/apps/basic/tsconfig.json
@@ -9,5 +9,5 @@
"sourceMap": true,
"moduleResolution": "bundler"
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/adapter-static/test/apps/spa/jsconfig.json b/packages/adapter-static/test/apps/spa/jsconfig.json
index e0733f11157e..681df462f63f 100644
--- a/packages/adapter-static/test/apps/spa/jsconfig.json
+++ b/packages/adapter-static/test/apps/spa/jsconfig.json
@@ -1,4 +1,4 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"]
}
diff --git a/packages/adapter-vercel/test/apps/basic/tsconfig.json b/packages/adapter-vercel/test/apps/basic/tsconfig.json
index 1de19ffb0ade..ca10a5d09eba 100644
--- a/packages/adapter-vercel/test/apps/basic/tsconfig.json
+++ b/packages/adapter-vercel/test/apps/basic/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"rewriteRelativeImportExtensions": true,
"allowJs": true,
diff --git a/packages/enhanced-img/test/apps/basics/jsconfig.json b/packages/enhanced-img/test/apps/basics/jsconfig.json
index 0247cc671b3a..1a1cacd4c31e 100644
--- a/packages/enhanced-img/test/apps/basics/jsconfig.json
+++ b/packages/enhanced-img/test/apps/basics/jsconfig.json
@@ -1,4 +1,4 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte", "*.js", "test/*.js"]
}
diff --git a/packages/kit/scripts/generate-dts.js b/packages/kit/scripts/generate-dts.js
index db78e8d968db..47ff7b3e2290 100644
--- a/packages/kit/scripts/generate-dts.js
+++ b/packages/kit/scripts/generate-dts.js
@@ -1,5 +1,5 @@
import { createBundle } from 'dts-buddy';
-import { readFileSync } from 'node:fs';
+import { readFileSync, writeFileSync } from 'node:fs';
await createBundle({
output: 'types/index.d.ts',
@@ -14,6 +14,7 @@ await createBundle({
'$app/navigation': 'src/runtime/app/navigation.js',
'$app/paths': 'src/runtime/app/paths/public.d.ts',
'$app/server': 'src/runtime/app/server/index.js',
+ '$app/service-worker': 'src/runtime/app/service-worker/index.js',
'$app/state': 'src/runtime/app/state/index.js'
},
include: ['src'],
@@ -24,10 +25,19 @@ await createBundle({
});
// dts-buddy doesn't inline imports of module declaration in ambient-private.d.ts but also doesn't include them, resulting in broken types - guard against that
-const types = readFileSync('./types/index.d.ts', 'utf-8');
+let types = readFileSync('types/index.d.ts', 'utf-8');
+
if (types.includes('__sveltekit/')) {
throw new Error(
'Found __sveltekit/ in types/index.d.ts - make sure to hide internal modules by not just reexporting them. Contents:\n\n' +
types
);
}
+
+// this line causes type-checking to fail — simplest fix is to ignore it
+types = types.replace(
+ 'export const self: ServiceWorkerGlobalScope;',
+ '// @ts-ignore\n\texport const self: ServiceWorkerGlobalScope;'
+);
+
+writeFileSync('types/index.d.ts', types);
diff --git a/packages/kit/src/cli.js b/packages/kit/src/cli.js
index eb99f6f257e8..fd7e219d9cc6 100755
--- a/packages/kit/src/cli.js
+++ b/packages/kit/src/cli.js
@@ -75,19 +75,19 @@ if (!command) {
}
if (command === 'sync') {
- // create placeholder .svelte-kit/tsconfig.json if necessary, to squelch warnings.
+ // create placeholder node_modules/$app/tsconfig/tsconfig.json if necessary, to squelch warnings.
// this isn't bulletproof — if someone has some esoteric config, it will continue
// to harmlessly warn — but we handle the 90% case and clean up after ourselves
- const sveltekit_dir = '.svelte-kit';
- const base_tsconfig = `${sveltekit_dir}/tsconfig.json`;
+ const dir = 'node_modules/$app/tsconfig';
+ const base_tsconfig = `${dir}/tsconfig.json`;
const base_tsconfig_json = '{}';
- const sveltekit_dir_exists = fs.existsSync(sveltekit_dir);
+ const sveltekit_dir_exists = fs.existsSync(dir);
const base_tsconfig_exists = fs.existsSync(base_tsconfig);
if (!base_tsconfig_exists) {
try {
- fs.mkdirSync('.svelte-kit');
+ fs.mkdirSync(dir, { recursive: true });
} catch {
// ignore
}
@@ -100,7 +100,7 @@ if (command === 'sync') {
const sveltekit_config = extract_svelte_config(vite_config);
const sync = await import('./core/sync/sync.js');
- sync.all_types(sveltekit_config);
+ sync.all_types(sveltekit_config, vite_config.root);
const explicit_env_entry = resolve_explicit_env_entry(sveltekit_config.kit);
await sync.env(sveltekit_config.kit, explicit_env_entry, vite_config.root, values.mode);
@@ -113,8 +113,8 @@ if (command === 'sync') {
fs.unlinkSync(base_tsconfig);
}
- if (!sveltekit_dir_exists && fs.readdirSync(sveltekit_dir).length === 0) {
- fs.rmSync(sveltekit_dir, { recursive: true });
+ if (!sveltekit_dir_exists && fs.readdirSync(dir).length === 0) {
+ fs.rmSync(dir, { recursive: true });
}
}
} else {
diff --git a/packages/kit/src/constants.js b/packages/kit/src/constants.js
index af1704f08390..de40d97c04f7 100644
--- a/packages/kit/src/constants.js
+++ b/packages/kit/src/constants.js
@@ -4,7 +4,7 @@
*/
export const SVELTE_KIT_ASSETS = '/_svelte_kit_assets';
-export const GENERATED_COMMENT = '// this file is generated — do not edit it\n';
+export const GENERATED_COMMENT = '// this file is generated — do not edit it';
export const ENDPOINT_METHODS = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD'];
diff --git a/packages/kit/src/core/config/options.js b/packages/kit/src/core/config/options.js
index 9a40a0734f6b..40ec63d6392d 100644
--- a/packages/kit/src/core/config/options.js
+++ b/packages/kit/src/core/config/options.js
@@ -286,9 +286,14 @@ export const validate_kit_options = object({
server: boolean(false)
}),
- typescript: object({
- config: fun((config) => config)
- }),
+ typescript: deprecate(
+ object({
+ config: fun((config) => config)
+ }),
+ (keypath) => {
+ return `The \`${keypath}\` option is deprecated, and will be removed in a future version. Add configuration to tsconfig.json directly`;
+ }
+ ),
version: object({
name: string(Date.now().toString()),
diff --git a/packages/kit/src/core/sync/sync.js b/packages/kit/src/core/sync/sync.js
index 0adf76e3c5ac..c4780f90ba22 100644
--- a/packages/kit/src/core/sync/sync.js
+++ b/packages/kit/src/core/sync/sync.js
@@ -1,11 +1,9 @@
import path from 'node:path';
-import process from 'node:process';
import create_manifest_data from './create_manifest_data/index.js';
import { write_client_manifest } from './write_client_manifest.js';
-import { write_tsconfig } from './write_tsconfig.js';
+import { write_tsconfig } from './write_tsconfig/index.js';
import { write_types, write_all_types } from './write_types/index.js';
-import { write_ambient } from './write_ambient.js';
-import { write_non_ambient } from './write_non_ambient.js';
+import { write_app_types } from './write_app_types.js';
import { write_server } from './write_server.js';
import {
create_node_analyser,
@@ -21,7 +19,6 @@ import { write_env } from './write_env.js';
*/
export function init(config, root) {
write_tsconfig(config.kit, root);
- write_ambient(config.kit);
}
/**
@@ -37,7 +34,7 @@ export function create(config, root) {
write_client_manifest(config.kit, manifest_data, `${output}/client`);
write_server(config, output, root);
write_all_types(config, manifest_data, root);
- write_non_ambient(config.kit, manifest_data);
+ write_app_types(config.kit, manifest_data, root);
return { manifest_data };
}
@@ -65,7 +62,7 @@ export function update(config, manifest_data, file, root) {
}
write_types(config, manifest_data, file, root);
- write_non_ambient(config.kit, manifest_data);
+ write_app_types(config.kit, manifest_data, root);
}
/**
@@ -81,13 +78,13 @@ export function all(config, root) {
/**
* Run sync.init and then generate all type files.
* @param {import('types').ValidatedConfig} config
+ * @param {string} root
*/
-export function all_types(config) {
- const cwd = process.cwd();
- init(config, cwd);
- const manifest_data = create_manifest_data({ config, cwd });
- write_all_types(config, manifest_data, cwd);
- write_non_ambient(config.kit, manifest_data);
+export function all_types(config, root) {
+ init(config, root);
+ const manifest_data = create_manifest_data({ config, cwd: root });
+ write_all_types(config, manifest_data, root);
+ write_app_types(config.kit, manifest_data, root);
}
/**
@@ -100,7 +97,7 @@ export function all_types(config) {
export async function env(kit, entry, root, mode) {
const env_config = await load_explicit_env(kit, entry, root, mode);
- write_env(kit, entry, env_config);
+ write_env(entry, env_config, root);
return env_config;
}
diff --git a/packages/kit/src/core/sync/write_ambient.js b/packages/kit/src/core/sync/write_ambient.js
deleted file mode 100644
index fe459c296801..000000000000
--- a/packages/kit/src/core/sync/write_ambient.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import path from 'node:path';
-import { GENERATED_COMMENT } from '../../constants.js';
-import { write_if_changed } from './utils.js';
-
-// TODO get rid of this, it's useless
-
-/**
- * Writes ambient declarations including types reference to @sveltejs/kit,
- * and the existing environment variables in process.env to
- * $env/static/private and $env/static/public
- * @param {import('types').ValidatedKitConfig} config
- */
-export function write_ambient(config) {
- /** @type {string} */
- const content = `${GENERATED_COMMENT}\n/// `;
-
- write_if_changed(path.join(config.outDir, 'ambient.d.ts'), content);
-}
diff --git a/packages/kit/src/core/sync/write_non_ambient.js b/packages/kit/src/core/sync/write_app_types.js
similarity index 91%
rename from packages/kit/src/core/sync/write_non_ambient.js
rename to packages/kit/src/core/sync/write_app_types.js
index 3010cc281f7f..d0a8c71614a9 100644
--- a/packages/kit/src/core/sync/write_non_ambient.js
+++ b/packages/kit/src/core/sync/write_app_types.js
@@ -51,8 +51,6 @@ function get_pathnames_for_trailing_slash(pathname, route) {
// people could use to type their own components.
// The T generic is needed or else there's a "all declarations must have identical type parameters" error.
const template = `
-${GENERATED_COMMENT}
-
declare module "svelte/elements" {
export interface HTMLAttributes {
'data-sveltekit-keepfocus'?: true | false | '' | undefined | null;
@@ -72,16 +70,15 @@ declare module "svelte/elements" {
'data-sveltekit-replacestate'?: true | false | '' | undefined | null;
}
}
-
-export {};
`;
/**
* Generate app types interface extension
* @param {import('types').ManifestData} manifest_data
* @param {import('types').ValidatedKitConfig} config
+ * @param {string} dir
*/
-function generate_app_types(manifest_data, config) {
+function generate_app_types(manifest_data, config, dir) {
/** @type {Map} */
const matcher_types = new Map();
@@ -96,7 +93,7 @@ function generate_app_types(manifest_data, config) {
resolve_entry(config.files.params) ??
config.files.params.replace(/\.(js|ts)$/, '') + '.js';
- return posixify(path.relative(config.outDir, params_file));
+ return posixify(path.relative(dir, params_file));
};
type = `import('@sveltejs/kit').MatcherParam<(typeof import('${path_to_params()}').params)[${JSON.stringify(matcher)}]>`;
@@ -253,13 +250,22 @@ function generate_app_types(manifest_data, config) {
}
/**
- * Writes non-ambient declarations to the output directory
+ * Writes `node_modules/$app/types/index.d.ts`. This file contains
+ * declarations for `$app/types` and `svelte/elements`, and
+ * imports `env.ts` which declares `$app/env/(public|private)`
* @param {import('types').ValidatedKitConfig} config
* @param {import('types').ManifestData} manifest_data
+ * @param {string} root
*/
-export function write_non_ambient(config, manifest_data) {
- const app_types = generate_app_types(manifest_data, config);
- const content = [template, app_types].join('\n\n');
+export function write_app_types(config, manifest_data, root) {
+ const dir = path.join(root, 'node_modules/$app/types');
+
+ const content = [
+ GENERATED_COMMENT,
+ `import '@sveltejs/kit';\nimport './env';`,
+ generate_app_types(manifest_data, config, dir),
+ template.trim()
+ ].join('\n\n');
- write_if_changed(path.join(config.outDir, 'non-ambient.d.ts'), content);
+ write_if_changed(path.join(dir, 'index.d.ts'), content);
}
diff --git a/packages/kit/src/core/sync/write_env.js b/packages/kit/src/core/sync/write_env.js
index 1a6946a81982..402e2ac6fb46 100644
--- a/packages/kit/src/core/sync/write_env.js
+++ b/packages/kit/src/core/sync/write_env.js
@@ -10,16 +10,18 @@ const DOCS = '// See https://svelte.dev/docs/kit/environment-variables for more
* Writes ambient declarations including types reference to @sveltejs/kit,
* and the existing environment variables in process.env to
* $env/static/private and $env/static/public
- * @param {import('types').ValidatedKitConfig} kit
* @param {string | null} entry
* @param {Record> | null} env_config
+ * @param {string} root
*/
-export function write_env(kit, entry, env_config) {
+export function write_env(entry, env_config, root) {
const content = [];
- const out = path.join(kit.outDir, 'env.d.ts');
+
+ const dir = path.join(root, 'node_modules/$app/types');
+ const out = path.join(dir, 'env.d.ts');
if (entry && env_config) {
- const relative = posixify(path.relative(kit.outDir, entry));
+ const relative = posixify(path.relative(dir, entry));
content.push(
`// This file is generated from ${relative}.\n${DOCS}`,
create_explicit_env_types(env_config, relative, 'private'),
diff --git a/packages/kit/src/core/sync/write_tsconfig.js b/packages/kit/src/core/sync/write_tsconfig.js
deleted file mode 100644
index 2f6b1eacc506..000000000000
--- a/packages/kit/src/core/sync/write_tsconfig.js
+++ /dev/null
@@ -1,258 +0,0 @@
-import fs from 'node:fs';
-import path from 'node:path';
-import { styleText } from 'node:util';
-import { posixify } from '../../utils/os.js';
-import { read_package_imports, normalize_import_value } from '../../utils/imports.js';
-import { write_if_changed } from './utils.js';
-
-/**
- * @param {string} cwd
- * @param {string} file
- */
-function maybe_file(cwd, file) {
- const resolved = path.resolve(cwd, file);
- if (fs.existsSync(resolved)) {
- return resolved;
- }
-}
-
-/**
- * @param {string} cwd
- * @param {string} file
- */
-function project_relative(cwd, file) {
- return posixify(path.relative(cwd, file));
-}
-
-/**
- * @param {string} file
- */
-function remove_trailing_slashstar(file) {
- if (file.endsWith('/*')) {
- return file.slice(0, -2);
- } else {
- return file;
- }
-}
-
-/**
- * Generates the tsconfig that the user's tsconfig inherits from.
- * @param {import('types').ValidatedKitConfig} kit
- * @param {string} cwd
- */
-export function write_tsconfig(kit, cwd) {
- const out = path.join(kit.outDir, 'tsconfig.json');
-
- const user_config = load_user_tsconfig(cwd);
- if (user_config) validate_user_config(cwd, out, user_config);
-
- write_if_changed(out, JSON.stringify(get_tsconfig(kit, cwd), null, '\t'));
-}
-
-/**
- * Generates the tsconfig that the user's tsconfig inherits from.
- * @param {import('types').ValidatedKitConfig} kit
- * @param {string} cwd
- */
-export function get_tsconfig(kit, cwd) {
- /** @param {string} file */
- const config_relative = (file) => posixify(path.relative(kit.outDir, file));
-
- const include = new Set([
- 'ambient.d.ts', // careful: changing this name would be a breaking change, because it's referenced in the service-workers documentation
- 'env.d.ts',
- 'non-ambient.d.ts',
- './types/**/$types.d.ts',
- config_relative('vite.config.js'),
- config_relative('vite.config.ts')
- ]);
- const src_includes = [kit.files.routes, kit.files.src].filter((dir) => {
- const relative = path.relative(kit.files.src, dir);
- return !relative || relative.startsWith('..');
- });
- for (const dir of src_includes) {
- include.add(config_relative(`${dir}/**/*.js`));
- include.add(config_relative(`${dir}/**/*.ts`));
- include.add(config_relative(`${dir}/**/*.svelte`));
- }
-
- // Test folder is a special case - we advocate putting tests in a top-level test folder
- // and it's not configurable (should we make it?)
- const test_folder = project_relative(cwd, 'test');
- include.add(config_relative(`${test_folder}/**/*.js`));
- include.add(config_relative(`${test_folder}/**/*.ts`));
- include.add(config_relative(`${test_folder}/**/*.svelte`));
- const tests_folder = project_relative(cwd, 'tests');
- include.add(config_relative(`${tests_folder}/**/*.js`));
- include.add(config_relative(`${tests_folder}/**/*.ts`));
- include.add(config_relative(`${tests_folder}/**/*.svelte`));
-
- const exclude = [config_relative('node_modules/**')];
- // Add service worker to exclude list so that worker types references in it don't spill over into the rest of the app
- // (i.e. suddenly ServiceWorkerGlobalScope would be available throughout the app, and some types might even clash)
- if (path.extname(kit.files.serviceWorker)) {
- exclude.push(config_relative(kit.files.serviceWorker));
- } else {
- exclude.push(config_relative(`${kit.files.serviceWorker}.js`));
- exclude.push(config_relative(`${kit.files.serviceWorker}/**/*.js`));
- exclude.push(config_relative(`${kit.files.serviceWorker}.ts`));
- exclude.push(config_relative(`${kit.files.serviceWorker}/**/*.ts`));
- exclude.push(config_relative(`${kit.files.serviceWorker}.d.ts`));
- exclude.push(config_relative(`${kit.files.serviceWorker}/**/*.d.ts`));
- }
-
- const config = {
- compilerOptions: {
- // generated options
- paths: {
- ...get_tsconfig_paths(kit, cwd),
- '$app/types': ['./types/index.d.ts']
- },
- rootDirs: [config_relative('.'), './types'],
-
- // essential options
- // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
- // to enforce using \`import type\` instead of \`import\` for Types.
- // Also, TypeScript doesn't know about import usages in the template because it only sees the
- // script of a Svelte file. Therefore preserve all value imports.
- verbatimModuleSyntax: true,
- // Vite compiles modules one at a time
- isolatedModules: true,
-
- // This is required for svelte-package to work as expected
- // Can be overwritten
- lib: ['esnext', 'DOM', 'DOM.Iterable'],
- moduleResolution: 'bundler',
- module: 'esnext',
- noEmit: true, // prevent tsconfig error "overwriting input files" - Vite handles the build and ignores this
- target: 'esnext'
- },
- include: [...include],
- exclude
- };
-
- return kit.typescript.config(config) ?? config;
-}
-
-/** @param {string} cwd */
-function load_user_tsconfig(cwd) {
- const file = maybe_file(cwd, 'tsconfig.json') || maybe_file(cwd, 'jsconfig.json');
-
- if (!file) return;
-
- // we have to eval the file, since it's not parseable as JSON (contains comments)
- const json = fs.readFileSync(file, 'utf-8');
-
- return {
- kind: path.basename(file),
- options: (0, eval)(`(${json})`)
- };
-}
-
-/**
- * @param {string} cwd
- * @param {string} out
- * @param {{ kind: string, options: any }} config
- */
-function validate_user_config(cwd, out, config) {
- // we need to check that the user's tsconfig extends the framework config
- const extend = config.options.extends;
- const extends_framework_config =
- typeof extend === 'string'
- ? path.resolve(cwd, extend) === out
- : Array.isArray(extend)
- ? extend.some((e) => path.resolve(cwd, e) === out)
- : false;
-
- const options = config.options.compilerOptions || {};
-
- if (extends_framework_config) {
- const { paths, baseUrl } = options;
-
- // TODO: baseUrl will be removed in TypeScript 7.0
- if (baseUrl || paths) {
- console.warn(
- styleText(
- ['bold', 'yellow'],
- `You have specified a baseUrl and/or paths in your ${config.kind} which interferes with SvelteKit's auto-generated tsconfig.json. ` +
- 'Remove it to avoid problems with intellisense. For path aliases, use subpath imports instead: https://svelte.dev/docs/kit/$lib'
- )
- );
- }
- } else {
- let relative = posixify(path.relative(cwd, out));
- if (!relative.startsWith('./')) relative = './' + relative;
-
- console.warn(
- styleText(
- ['bold', 'yellow'],
- `Your ${config.kind} should extend the configuration generated by SvelteKit:`
- )
- );
- console.warn(`{\n "extends": "${relative}"\n}`);
- }
-}
-
-//
-const alias_regex = /^(.+?)(\/\*)?$/;
-//
-const value_regex = /^(.*?)((\/\*)|(\.\w+))?$/;
-
-/**
- * Generates tsconfig path aliases from kit's aliases and the package.json `imports` field.
- * Related to vite alias creation.
- *
- * @param {import('types').ValidatedKitConfig} config
- * @param {string} cwd
- */
-function get_tsconfig_paths(config, cwd) {
- /** @param {string} file */
- const config_relative = (file) => {
- let relative_path = path.relative(config.outDir, file);
- if (!relative_path.startsWith('..')) {
- relative_path = './' + relative_path;
- }
- return posixify(relative_path);
- };
-
- const alias = { ...config.alias };
-
- // Add all `#`-prefixed imports from package.json as path aliases
- const imports = read_package_imports(cwd);
- if (imports) {
- for (const [key, raw_value] of Object.entries(imports)) {
- if (!key.startsWith('#')) continue;
- const value = normalize_import_value(raw_value);
- if (value) {
- alias[key] = value;
- }
- }
- }
-
- /** @type {Record} */
- const paths = {};
-
- for (const [key, value] of Object.entries(alias)) {
- const key_match = alias_regex.exec(key);
- if (!key_match) throw new Error(`Invalid alias key: ${key}`);
-
- const value_match = value_regex.exec(value);
- if (!value_match) throw new Error(`Invalid alias value: ${value}`);
-
- const rel_path = config_relative(remove_trailing_slashstar(value));
- const slashstar = key_match[2];
-
- if (slashstar) {
- paths[key] = [rel_path + '/*'];
- } else {
- paths[key] = [rel_path];
- const fileending = value_match[4];
-
- if (!fileending && !(key + '/*' in alias)) {
- paths[key + '/*'] = [rel_path + '/*'];
- }
- }
- }
-
- return paths;
-}
diff --git a/packages/kit/src/core/sync/write_tsconfig.spec.js b/packages/kit/src/core/sync/write_tsconfig.spec.js
deleted file mode 100644
index 3234717dfecc..000000000000
--- a/packages/kit/src/core/sync/write_tsconfig.spec.js
+++ /dev/null
@@ -1,114 +0,0 @@
-import { assert, expect, test } from 'vitest';
-import { validate_config } from '../config/index.js';
-import { get_tsconfig } from './write_tsconfig.js';
-
-test('Creates tsconfig path aliases from kit.alias', () => {
- const { kit } = validate_config({
- kit: {
- alias: {
- simpleKey: 'simple/value',
- key: 'value',
- 'key/*': 'some/other/value/*',
- keyToFile: 'path/to/file.ts',
- $routes: '.svelte-kit/types/src/routes'
- }
- }
- });
-
- // Use a cwd without a package.json so no `#`-prefixed imports are picked up
- const { compilerOptions } = get_tsconfig(kit, import.meta.dirname);
-
- // No `#`-prefixed path aliases because the package.json at the test cwd
- // doesn't have an `imports` field
- expect(compilerOptions.paths).toEqual({
- '$app/types': ['./types/index.d.ts'],
- simpleKey: ['../simple/value'],
- 'simpleKey/*': ['../simple/value/*'],
- key: ['../value'],
- 'key/*': ['../some/other/value/*'],
- keyToFile: ['../path/to/file.ts'],
- $routes: ['./types/src/routes'],
- '$routes/*': ['./types/src/routes/*']
- });
-});
-
-test('Creates tsconfig path aliases from package.json import map', () => {
- const { kit } = validate_config({});
- const { compilerOptions } = get_tsconfig(kit, import.meta.dirname + '/write_tsconfig_test');
-
- // No `#`-prefixed path aliases because the package.json at the test cwd
- // doesn't have an `imports` field
- expect(compilerOptions.paths).toEqual({
- '$app/types': ['./types/index.d.ts'],
- '#lib': ['../src/lib'],
- '#lib/*': ['../src/lib/*']
- });
-});
-
-test('Allows generated tsconfig to be mutated', () => {
- const { kit } = validate_config({
- kit: {
- typescript: {
- config: (config) => {
- config.extends = 'some/other/tsconfig.json';
- }
- }
- }
- });
-
- const config = get_tsconfig(kit, '.');
-
- // @ts-expect-error
- assert.equal(config.extends, 'some/other/tsconfig.json');
-});
-
-test('Allows generated tsconfig to be replaced', () => {
- const { kit } = validate_config({
- kit: {
- typescript: {
- config: (config) => ({
- ...config,
- extends: 'some/other/tsconfig.json'
- })
- }
- }
- });
-
- const config = get_tsconfig(kit, '.');
-
- // @ts-expect-error
- assert.equal(config.extends, 'some/other/tsconfig.json');
-});
-
-test('Creates tsconfig include from kit.files', () => {
- const { kit } = validate_config({
- kit: {
- files: {
- routes: 'app'
- }
- }
- });
-
- const { include } = get_tsconfig(kit, '.');
-
- expect(include).toEqual([
- 'ambient.d.ts',
- 'env.d.ts',
- 'non-ambient.d.ts',
- './types/**/$types.d.ts',
- '../vite.config.js',
- '../vite.config.ts',
- '../app/**/*.js',
- '../app/**/*.ts',
- '../app/**/*.svelte',
- '../src/**/*.js',
- '../src/**/*.ts',
- '../src/**/*.svelte',
- '../test/**/*.js',
- '../test/**/*.ts',
- '../test/**/*.svelte',
- '../tests/**/*.js',
- '../tests/**/*.ts',
- '../tests/**/*.svelte'
- ]);
-});
diff --git a/packages/kit/src/core/sync/write_tsconfig/index.js b/packages/kit/src/core/sync/write_tsconfig/index.js
new file mode 100644
index 000000000000..28b6a19dbb91
--- /dev/null
+++ b/packages/kit/src/core/sync/write_tsconfig/index.js
@@ -0,0 +1,245 @@
+/** @import { ValidatedKitConfig } from 'types' */
+import process from 'node:process';
+import fs from 'node:fs';
+import path from 'node:path';
+import ts from 'typescript';
+import { styleText } from 'node:util';
+import { write_if_changed } from '../utils.js';
+import {
+ ESSENTIAL_OPTIONS,
+ extends_id,
+ get_subpath_imports,
+ normalize_config,
+ RECOMMENDED_OPTIONS,
+ remove_trailing_slashstar,
+ validate_resolved_config
+} from './utils.js';
+
+/**
+ * Generates the tsconfig that the user's tsconfig inherits from.
+ * @param {import('types').ValidatedKitConfig} kit
+ * @param {string} root
+ */
+export function write_tsconfig(kit, root) {
+ const paths = get_paths(kit, root);
+
+ write_parent_tsconfig(
+ root,
+ root,
+ '$app/tsconfig',
+ {
+ compilerOptions: {
+ paths,
+ rootDirs: ['.', `${kit.outDir}/types`],
+ types: ['$app/types'],
+
+ // This is required for svelte-package to work as expected
+ // Can be overwritten
+ lib: ['ESNext', 'DOM', 'DOM.Iterable'],
+
+ ...ESSENTIAL_OPTIONS,
+ ...RECOMMENDED_OPTIONS
+ },
+ exclude: [kit.files.serviceWorker]
+ },
+ {
+ extends: '$app/tsconfig',
+ include: ['src']
+ },
+ kit.typescript.config
+ );
+
+ write_parent_tsconfig(
+ root,
+ kit.files.serviceWorker,
+ '$app/tsconfig/service-worker',
+ {
+ compilerOptions: {
+ paths,
+ types: ['$app/types'],
+ lib: ['ESNext', 'WebWorker'],
+ ...ESSENTIAL_OPTIONS
+ }
+ },
+ {
+ extends: '$app/tsconfig/service-worker'
+ }
+ );
+}
+
+/**
+ * Write a generated `tsconfig.json` inside `node_modules`, for the
+ * user config to extend
+ * @param {string} root The project root
+ * @param {string} dir The directory to resolve a user config from
+ * @param {string} id The id of the generated config
+ * @param {any} config The contents of the generated tsconfig, with paths relative to `root`
+ * @param {any} example What to print if the user config does _not_ extend the generated config
+ * @param {ValidatedKitConfig['typescript']['config']} [transform] TODO get rid of this
+ */
+function write_parent_tsconfig(root, dir, id, config, example, transform) {
+ const out_file = path.join(root, `node_modules/${id}/tsconfig.json`);
+
+ let normalized = normalize_config(out_file, config);
+ normalized = transform?.(normalized) ?? normalized;
+
+ write_if_changed(out_file, JSON.stringify(normalized, null, '\t'));
+
+ const user_config = load_user_tsconfig(dir);
+
+ if (user_config && modified_since_last_check(user_config.file)) {
+ // now that we've written the parent config, we can resolve the
+ // user config and validate that nothing important was overwritten
+ if (!extends_id(user_config.options, id)) {
+ console.warn(
+ styleText(
+ ['bold', 'yellow'],
+ `${path.relative(process.cwd(), user_config.file)} should extend SvelteKit's built-in configuration:`
+ )
+ );
+
+ console.warn(JSON.stringify(example, null, ' '));
+
+ return;
+ }
+
+ const resolved = ts.parseJsonConfigFileContent(user_config.options, ts.sys, dir).options;
+ const warnings = validate_resolved_config(resolved, config.compilerOptions);
+
+ if (warnings.length > 0) {
+ console.warn(
+ styleText(
+ ['bold', 'yellow'],
+ `Found issues while validating ${path.relative(process.cwd(), user_config.file)}`
+ )
+ );
+
+ for (const warning of warnings) {
+ console.warn(` - ${warning}`);
+ }
+ }
+ }
+}
+
+/** @type {Map} */
+const mtimes = new Map();
+
+/**
+ * Returns true if the file was modified since we last got here,
+ * otherwise we can skip warnings
+ * @param {string} file
+ */
+function modified_since_last_check(file) {
+ const a = mtimes.get(file) ?? -1;
+ const b = fs.statSync(file).mtimeMs;
+ mtimes.set(file, b);
+
+ return b > a;
+}
+
+/**
+ * @param {string} cwd
+ * @param {string} file
+ */
+function maybe_file(cwd, file) {
+ const resolved = path.resolve(cwd, file);
+ if (fs.existsSync(resolved)) {
+ return resolved;
+ }
+}
+
+/** @param {string} cwd */
+function load_user_tsconfig(cwd) {
+ const file = maybe_file(cwd, 'tsconfig.json') || maybe_file(cwd, 'jsconfig.json');
+ if (!file) return;
+
+ const options = load_tsconfig(file);
+
+ return {
+ file,
+ kind: path.basename(file),
+ options
+ };
+}
+
+/**
+ * @param {string} file
+ */
+function load_tsconfig(file) {
+ const options = ts.readConfigFile(file, ts.sys.readFile);
+
+ if (options.error) {
+ let message = `Failed to parse TypeScript config`;
+
+ if (typeof options.error.messageText === 'string') {
+ message += `: ${options.error.messageText}`;
+ }
+
+ const error = new Error(message);
+ error.stack = '';
+
+ if (options.error.file && options.error.start !== undefined) {
+ const line_start = options.error.file.text.lastIndexOf('\n', options.error.start);
+
+ const line =
+ line_start === -1
+ ? 0
+ : options.error.file.text.slice(0, options.error.start).split('\n').length;
+ const column = options.error.start - line_start;
+
+ error.stack = `${error.message}\n at ${path.relative(process.cwd(), file)}:${line}:${column}`;
+ }
+
+ throw error;
+ }
+
+ return options.config;
+}
+
+/** Matches anything (except the empty string/string with newline), and separates any trailing `/*` */
+const alias_key = /^(.+?)(\/\*)?$/;
+
+/** Matches anything (except the empty string/string with newline), and separates any trailing `/*` or file extension */
+const alias_value = /^(.+?)((\/\*)|(\.\w+))?$/;
+
+/**
+ * Generates tsconfig path aliases from kit's aliases and the package.json `imports` field.
+ * Related to vite alias creation.
+ *
+ * @param {import('types').ValidatedKitConfig} config
+ * @param {string} root
+ * @returns {Record}
+ */
+function get_paths(config, root) {
+ const alias = {
+ ...config.alias,
+ ...get_subpath_imports(root)
+ };
+
+ /** @type {Record} */
+ const paths = {};
+
+ for (const [key, value] of Object.entries(alias)) {
+ const key_match = alias_key.exec(key);
+ if (!key_match) throw new Error(`Invalid alias key: ${key}`);
+
+ const value_match = alias_value.exec(value);
+ if (!value_match) throw new Error(`Invalid alias value: ${value}`);
+
+ const resolved = path.resolve(root, remove_trailing_slashstar(value));
+ const slashstar = key_match[2];
+
+ if (slashstar) {
+ paths[key] = [resolved + '/*'];
+ } else {
+ paths[key] = [resolved];
+ const fileending = value_match[4];
+
+ if (!fileending && !(key + '/*' in alias)) {
+ paths[key + '/*'] = [resolved + '/*'];
+ }
+ }
+ }
+
+ return paths;
+}
diff --git a/packages/kit/src/core/sync/write_tsconfig_test/package.json b/packages/kit/src/core/sync/write_tsconfig/test-app/package.json
similarity index 100%
rename from packages/kit/src/core/sync/write_tsconfig_test/package.json
rename to packages/kit/src/core/sync/write_tsconfig/test-app/package.json
diff --git a/packages/kit/src/core/sync/write_tsconfig/utils.js b/packages/kit/src/core/sync/write_tsconfig/utils.js
new file mode 100644
index 000000000000..472b402b499f
--- /dev/null
+++ b/packages/kit/src/core/sync/write_tsconfig/utils.js
@@ -0,0 +1,162 @@
+import path from 'node:path';
+import { normalize_import_value, read_package_imports } from '../../../utils/imports.js';
+import { posixify } from '../../../utils/os.js';
+
+/**
+ * Without these, compilation will fail
+ * @type {Record}
+ */
+export const ESSENTIAL_OPTIONS = {
+ // svelte-preprocess cannot figure out whether you have a value or a type, so tell TypeScript
+ // to enforce using \`import type\` instead of \`import\` for Types.
+ // Also, TypeScript doesn't know about import usages in the template because it only sees the
+ // script of a Svelte file. Therefore preserve all value imports.
+ verbatimModuleSyntax: true,
+ // Vite compiles modules one at a time
+ isolatedModules: true
+};
+
+/**
+ * Options that are strongly recommended, either because not having them is silly or
+ * because the align TypeScript's behaviour with Vite's, but which can be overwritten
+ * @type {Record}
+ */
+export const RECOMMENDED_OPTIONS = {
+ allowJs: true,
+ checkJs: true,
+ forceConsistentCasingInFileNames: true,
+ resolveJsonModule: true,
+ moduleDetection: 'force',
+ moduleResolution: 'bundler',
+ allowImportingTsExtensions: true,
+ module: 'esnext',
+ target: 'esnext',
+ skipLibCheck: true,
+ esModuleInterop: true,
+ noEmit: true // prevent tsconfig error "overwriting input files" - Vite handles the build and ignores this
+};
+
+/**
+ * Validates that a tsconfig contains `"extends": ""`
+ * @param {any} options
+ * @param {string} id
+ */
+export function extends_id(options, id) {
+ const o = options.extends;
+ return Array.isArray(o) ? o.includes(id) : o === id;
+}
+
+/**
+ * Makes paths relative to the output file
+ * @param {string} out
+ * @param {any} config
+ * @param {(input: any) => any} [transform] TODO get rid of this option
+ */
+export function normalize_config(out, config, transform) {
+ const dir = path.dirname(out);
+ const relative = (/** @type {string} */ file) => posixify(path.relative(dir, file));
+
+ const paths =
+ config.compilerOptions?.paths &&
+ Object.fromEntries(
+ Object.entries(config.compilerOptions?.paths).map(([k, v]) => [k, v.map(relative)])
+ );
+
+ const normalized = {
+ ...config,
+ compilerOptions: {
+ ...config.compilerOptions,
+ paths,
+ rootDirs: config.compilerOptions?.rootDirs?.map(relative)
+ },
+ include: config.include?.map(relative),
+ exclude: config.exclude?.map(relative)
+ };
+
+ return transform?.(normalized) ?? normalized;
+}
+
+/**
+ * @param {any} resolved
+ * @param {any} options
+ */
+export function validate_resolved_config(resolved, options) {
+ const warnings = [];
+
+ const join = (/** @type {string[]} */ array) =>
+ array
+ .map((v) => JSON.stringify(v))
+ .join(', ')
+ .replace(/, ([^,]*)$/, ' and $1');
+
+ const missing_types = options.types?.filter(
+ (/** @type {string} */ type) => !resolved.types?.includes(type)
+ );
+
+ if (missing_types.length > 0) {
+ warnings.push(`"types" was overwritten. It must include ${join(missing_types)}`);
+ }
+
+ if (options.paths) {
+ /** @type {Set} */
+ const mismatch = new Set();
+
+ for (const [k, expected] of Object.entries(options.paths)) {
+ const actual = resolved.paths?.[k]?.map((/** @type {string} */ x) =>
+ path.resolve(/** @type {string} */ (resolved.pathsBasePath), x)
+ );
+
+ if (JSON.stringify(expected) !== JSON.stringify(actual)) {
+ mismatch.add(remove_trailing_slashstar(k));
+ }
+ }
+
+ if (mismatch.size > 0) {
+ const joined = join([...mismatch]);
+
+ warnings.push(`"paths" was overwritten. Imports from ${joined} may not typecheck`);
+ }
+ }
+
+ for (const key in ESSENTIAL_OPTIONS) {
+ if (resolved[key] !== options[key]) {
+ warnings.push(`"${key}" was overwritten. It should be ${JSON.stringify(options[key])}`);
+ }
+ }
+
+ return warnings;
+}
+
+/**
+ * @param {string} file
+ */
+export function remove_trailing_slashstar(file) {
+ if (file.endsWith('/*')) {
+ return file.slice(0, -2);
+ } else {
+ return file;
+ }
+}
+
+/**
+ * @param {string} root
+ */
+export function get_subpath_imports(root) {
+ // Add all `#`-prefixed imports from package.json as path aliases
+ const imports = read_package_imports(root);
+
+ /** @type {Record} */
+ const alias = {};
+
+ if (imports) {
+ for (const [key, raw_value] of Object.entries(imports)) {
+ if (!key.startsWith('#')) continue;
+ const value = normalize_import_value(raw_value);
+ if (value) {
+ alias[key] = value;
+ }
+ }
+ }
+
+ return alias;
+}
diff --git a/packages/kit/src/core/sync/write_tsconfig/utils.spec.js b/packages/kit/src/core/sync/write_tsconfig/utils.spec.js
new file mode 100644
index 000000000000..5a3cf7af73b8
--- /dev/null
+++ b/packages/kit/src/core/sync/write_tsconfig/utils.spec.js
@@ -0,0 +1,99 @@
+import { assert, describe, test } from 'vitest';
+import {
+ extends_id,
+ get_subpath_imports,
+ normalize_config,
+ validate_resolved_config
+} from './utils.js';
+
+describe('normalize_config', () => {
+ test('normalizes rootDirs', () => {
+ assert.deepEqual(
+ normalize_config('/path/to/tsconfig.json', {
+ compilerOptions: {
+ rootDirs: ['/path/of/my/src', '/path/of/my/generated/stuff']
+ }
+ }).compilerOptions.rootDirs,
+ ['../of/my/src', '../of/my/generated/stuff']
+ );
+ });
+
+ test('normalizes paths', () => {
+ assert.deepEqual(
+ normalize_config('/path/to/tsconfig.json', {
+ compilerOptions: {
+ paths: {
+ '#x': ['/path/of/my/alias']
+ }
+ }
+ }).compilerOptions.paths,
+ {
+ '#x': ['../of/my/alias']
+ }
+ );
+ });
+
+ test('mutates a config', () => {
+ const { a, b } = normalize_config(
+ '/doesnt/matter',
+ {
+ a: 1,
+ b: 2
+ },
+ (config) => {
+ config.b += 1;
+ }
+ );
+
+ assert.equal(a, 1);
+ assert.equal(b, 3);
+ });
+
+ test('replaces a config', () => {
+ const { a, b, c } = normalize_config(
+ '/doesnt/matter',
+ {
+ a: 1,
+ b: 2
+ },
+ (config) => ({ ...config, c: 3 })
+ );
+
+ assert.equal(a, 1);
+ assert.equal(b, 2);
+ assert.equal(c, 3);
+ });
+});
+
+describe('get_subpath_imports', () => {
+ test('gets normalized subpath imports', () => {
+ assert.deepEqual(get_subpath_imports(import.meta.dirname + '/test-app'), {
+ '#lib': 'src/lib',
+ '#lib/*': 'src/lib/*'
+ });
+ });
+});
+
+describe('extends_id', () => {
+ const id = 'POTATO';
+
+ test('validates that a config extends an id (string)', () => {
+ assert.equal(extends_id({ extends: id }, id), true);
+ });
+
+ test('validates that a config extends an id (array)', () => {
+ assert.equal(extends_id({ extends: [id] }, id), true);
+ });
+
+ test('validates that a config does not extend an id', () => {
+ assert.equal(extends_id({}, id), false);
+ });
+});
+
+describe('validate_resolved_config', () => {
+ test('warns if types is overwritten', () => {
+ const warnings = validate_resolved_config({ types: [] }, { types: ['pinky', 'perky'] });
+
+ assert.deepEqual(warnings, ['"types" was overwritten. It must include "pinky" and "perky"']);
+ });
+});
diff --git a/packages/kit/src/core/sync/write_types/index.js b/packages/kit/src/core/sync/write_types/index.js
index 05b4561a240b..184766fbf85c 100644
--- a/packages/kit/src/core/sync/write_types/index.js
+++ b/packages/kit/src/core/sync/write_types/index.js
@@ -609,7 +609,7 @@ function generate_params_type(params, outdir, config) {
(param) =>
`${/^\w+$/.test(param.name) ? param.name : `'${param.name}'`}${param.optional ? '?' : ''}: ${
param.matcher
- ? `import('@sveltejs/kit').MatcherParam<(typeof import('${params_import}').params)[${JSON.stringify(param.matcher)}]>`
+ ? `Kit.MatcherParam<(typeof import('${params_import}').params)[${JSON.stringify(param.matcher)}]>`
: 'string'
}${param.optional ? ' | undefined' : ''}`
)
diff --git a/packages/kit/src/core/sync/write_types/index.spec.js b/packages/kit/src/core/sync/write_types/index.spec.js
index 90fd987ac6c8..cc6e369de4ba 100644
--- a/packages/kit/src/core/sync/write_types/index.spec.js
+++ b/packages/kit/src/core/sync/write_types/index.spec.js
@@ -2,12 +2,13 @@ import { execSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import process from 'node:process';
-import { assert, expect, test } from 'vitest';
+import { assert, describe, expect, test } from 'vitest';
import { rimraf } from '../../../utils/filesystem.js';
import create_manifest_data from '../create_manifest_data/index.js';
import { tweak_types, write_all_types } from './index.js';
-import { write_non_ambient } from '../write_non_ambient.js';
+import { write_app_types } from '../write_app_types.js';
import { validate_config } from '../../config/index.js';
+import { write_env } from '../write_env.js';
const cwd = path.join(import.meta.dirname, 'test');
@@ -32,10 +33,11 @@ function run_test(dir) {
});
write_all_types(initial, manifest, root);
- write_non_ambient(initial.kit, manifest);
+ write_app_types(initial.kit, manifest, root);
+ write_env('', {}, root);
}
-test('Creates correct $types', { timeout: 60000 }, () => {
+describe('Creates correct $types', () => {
// To save us from creating a real SvelteKit project for each of the tests,
// we first run the type generation directly for each test case, and then
// call `tsc` to check that the generated types are valid.
@@ -44,17 +46,20 @@ test('Creates correct $types', { timeout: 60000 }, () => {
.filter((dir) => fs.statSync(`${cwd}/${dir}`).isDirectory());
for (const dir of directories) {
- run_test(dir);
- try {
- // we skip lib check if MATRIX_VITE is set and not 'current' because overrides for vite can cause type mismatches
- const skipLibCheck = process.env.MATRIX_VITE != null && process.env.MATRIX_VITE !== 'current';
- execSync(`pnpm testtypes${skipLibCheck ? ' --skipLibCheck' : ''}`, {
- cwd: path.join(cwd, dir)
- });
- } catch (e) {
- console.error(/** @type {any} */ (e).stdout.toString());
- throw new Error(`${dir} type tests failed`, { cause: e });
- }
+ test(dir, { timeout: 60000 }, () => {
+ run_test(dir);
+ try {
+ // we skip lib check if MATRIX_VITE is set and not 'current' because overrides for vite can cause type mismatches
+ const skipLibCheck =
+ process.env.MATRIX_VITE != null && process.env.MATRIX_VITE !== 'current';
+ execSync(`pnpm testtypes${skipLibCheck ? ' --skipLibCheck' : ''}`, {
+ cwd: path.join(cwd, dir)
+ });
+ } catch (e) {
+ console.error(/** @type {any} */ (e).stdout.toString());
+ throw e;
+ }
+ });
}
});
diff --git a/packages/kit/src/core/sync/write_types/test/app-types/tsconfig.json b/packages/kit/src/core/sync/write_types/test/app-types/tsconfig.json
index efd87c2e6516..db3fd82b4d9d 100644
--- a/packages/kit/src/core/sync/write_types/test/app-types/tsconfig.json
+++ b/packages/kit/src/core/sync/write_types/test/app-types/tsconfig.json
@@ -15,6 +15,6 @@
},
"types": ["node"]
},
- "include": ["./**/*.js", "./**/*.ts", ".svelte-kit/non-ambient.d.ts"],
+ "include": ["./**/*.js", "./**/*.ts", ".svelte-kit/non-ambient.d.ts", "node_modules/$app/types"],
"exclude": ["..svelte-kit/**"]
}
diff --git a/packages/kit/src/core/sync/write_types/test/param-type-inference/tsconfig.json b/packages/kit/src/core/sync/write_types/test/param-type-inference/tsconfig.json
index a65da5db03a8..f477e5594cef 100644
--- a/packages/kit/src/core/sync/write_types/test/param-type-inference/tsconfig.json
+++ b/packages/kit/src/core/sync/write_types/test/param-type-inference/tsconfig.json
@@ -14,6 +14,6 @@
},
"types": ["node"]
},
- "include": ["./**/*.js", "./**/*.ts", ".svelte-kit/non-ambient.d.ts"],
+ "include": ["./**/*.js", "./**/*.ts", ".svelte-kit/non-ambient.d.ts", "node_modules/$app/types"],
"exclude": ["..svelte-kit/**"]
}
diff --git a/packages/kit/src/exports/public.d.ts b/packages/kit/src/exports/public.d.ts
index c1f28309fa6f..e92f0aa0ccad 100644
--- a/packages/kit/src/exports/public.d.ts
+++ b/packages/kit/src/exports/public.d.ts
@@ -861,6 +861,9 @@ export interface KitConfig {
*/
server?: boolean;
};
+ /**
+ * @deprecated Add configuration to `tsconfig.json` directly
+ */
typescript?: {
/**
* A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.
diff --git a/packages/kit/src/runtime/app/service-worker/index.js b/packages/kit/src/runtime/app/service-worker/index.js
new file mode 100644
index 000000000000..88c1981a8a4d
--- /dev/null
+++ b/packages/kit/src/runtime/app/service-worker/index.js
@@ -0,0 +1,24 @@
+///
+///
+///
+
+import { DEV } from 'esm-env';
+
+/**
+ * The execution context of a service worker. This export exists to make it easier to
+ * use service workers with the correct types, provided the importing module is governed
+ * by a `tsconfig.json` that extends [`$app/tsconfig/service-worker`](https://svelte.dev/docs/kit/$app-tsconfig-service-worker).
+ *
+ */
+export const self = /** @type {ServiceWorkerGlobalScope} */ (
+ /** @type {unknown} */ (globalThis.self)
+);
+
+if (DEV) {
+ if (
+ typeof ServiceWorkerGlobalScope === 'undefined' ||
+ !(self instanceof ServiceWorkerGlobalScope)
+ ) {
+ throw new Error('The `$app/service-worker` module can only be imported into a service worker');
+ }
+}
diff --git a/packages/kit/test/apps/amp/tsconfig.json b/packages/kit/test/apps/amp/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/apps/amp/tsconfig.json
+++ b/packages/kit/test/apps/amp/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/async/tsconfig.json b/packages/kit/test/apps/async/tsconfig.json
index 85de622f1339..a453d154033b 100644
--- a/packages/kit/test/apps/async/tsconfig.json
+++ b/packages/kit/test/apps/async/tsconfig.json
@@ -6,5 +6,6 @@
"resolveJsonModule": true,
"rewriteRelativeImportExtensions": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "include": ["src", "unit-test", "test", "playwright.config.js"],
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/async/vite.config.js b/packages/kit/test/apps/async/vite.config.js
index be69d36e3c2c..d01ad4345392 100644
--- a/packages/kit/test/apps/async/vite.config.js
+++ b/packages/kit/test/apps/async/vite.config.js
@@ -18,11 +18,6 @@ export default defineConfig({
experimental: {
remoteFunctions: true,
forkPreloads: true
- },
- typescript: {
- config(config) {
- config.include.push('../unit-test/*.js', '../test/*.js', '../playwright.config.js');
- }
}
})
],
diff --git a/packages/kit/test/apps/basics/tsconfig.json b/packages/kit/test/apps/basics/tsconfig.json
index 4cf3b2a60fe9..9c01fc02941d 100644
--- a/packages/kit/test/apps/basics/tsconfig.json
+++ b/packages/kit/test/apps/basics/tsconfig.json
@@ -5,5 +5,6 @@
"esModuleInterop": true,
"resolveJsonModule": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig",
+ "include": ["src", "unit-test", "test"]
}
diff --git a/packages/kit/test/apps/basics/vite.config.js b/packages/kit/test/apps/basics/vite.config.js
index 3d5dd55594df..2de19ee19ecf 100644
--- a/packages/kit/test/apps/basics/vite.config.js
+++ b/packages/kit/test/apps/basics/vite.config.js
@@ -84,12 +84,6 @@ export default defineConfig({
router: {
resolution: /** @type {'client' | 'server'} */ (process.env.ROUTER_RESOLUTION) || 'client'
- },
-
- typescript: {
- config: (config) => {
- config.include.push('../unit-test');
- }
}
})
],
diff --git a/packages/kit/test/apps/dev-only/tsconfig.json b/packages/kit/test/apps/dev-only/tsconfig.json
index 4cf3b2a60fe9..947c9ce5579f 100644
--- a/packages/kit/test/apps/dev-only/tsconfig.json
+++ b/packages/kit/test/apps/dev-only/tsconfig.json
@@ -5,5 +5,5 @@
"esModuleInterop": true,
"resolveJsonModule": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/embed/tsconfig.json b/packages/kit/test/apps/embed/tsconfig.json
index 1d665886266b..5dd124a3b02a 100644
--- a/packages/kit/test/apps/embed/tsconfig.json
+++ b/packages/kit/test/apps/embed/tsconfig.json
@@ -6,5 +6,5 @@
"noEmit": true,
"resolveJsonModule": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/hash-based-routing/tsconfig.json b/packages/kit/test/apps/hash-based-routing/tsconfig.json
index 1d665886266b..5dd124a3b02a 100644
--- a/packages/kit/test/apps/hash-based-routing/tsconfig.json
+++ b/packages/kit/test/apps/hash-based-routing/tsconfig.json
@@ -6,5 +6,5 @@
"noEmit": true,
"resolveJsonModule": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/no-ssr/tsconfig.json b/packages/kit/test/apps/no-ssr/tsconfig.json
index 1d665886266b..5dd124a3b02a 100644
--- a/packages/kit/test/apps/no-ssr/tsconfig.json
+++ b/packages/kit/test/apps/no-ssr/tsconfig.json
@@ -6,5 +6,5 @@
"noEmit": true,
"resolveJsonModule": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/options-2/tsconfig.json b/packages/kit/test/apps/options-2/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/apps/options-2/tsconfig.json
+++ b/packages/kit/test/apps/options-2/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/options-3/tsconfig.json b/packages/kit/test/apps/options-3/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/apps/options-3/tsconfig.json
+++ b/packages/kit/test/apps/options-3/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/apps/options/tsconfig.json b/packages/kit/test/apps/options/tsconfig.json
index 77500cd3c88f..6e016144f94c 100644
--- a/packages/kit/test/apps/options/tsconfig.json
+++ b/packages/kit/test/apps/options/tsconfig.json
@@ -4,5 +4,6 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.custom-out-dir/tsconfig.json"
+ "extends": "$app/tsconfig",
+ "include": ["source", "test", "vite.custom.config.js", "playwright.config.js"]
}
diff --git a/packages/kit/test/apps/options/vite.custom.config.js b/packages/kit/test/apps/options/vite.custom.config.js
index 9ac7b8a758ef..8b0615458441 100644
--- a/packages/kit/test/apps/options/vite.custom.config.js
+++ b/packages/kit/test/apps/options/vite.custom.config.js
@@ -51,11 +51,6 @@ const config = {
},
router: {
resolution: /** @type {'client' | 'server'} */ (process.env.ROUTER_RESOLUTION) || 'client'
- },
- typescript: {
- config(config) {
- config.include.push('../vite.custom.config.js', '../playwright.config.js');
- }
}
})
],
diff --git a/packages/kit/test/apps/prerendered-app-error-pages/jsconfig.json b/packages/kit/test/apps/prerendered-app-error-pages/jsconfig.json
index 5c34c9263152..726d608e639d 100644
--- a/packages/kit/test/apps/prerendered-app-error-pages/jsconfig.json
+++ b/packages/kit/test/apps/prerendered-app-error-pages/jsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/apps/writes/tsconfig.json b/packages/kit/test/apps/writes/tsconfig.json
index 1d665886266b..5dd124a3b02a 100644
--- a/packages/kit/test/apps/writes/tsconfig.json
+++ b/packages/kit/test/apps/writes/tsconfig.json
@@ -6,5 +6,5 @@
"noEmit": true,
"resolveJsonModule": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/build-errors/apps/env-private-dynamic/tsconfig.json b/packages/kit/test/build-errors/apps/env-private-dynamic/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/env-private-dynamic/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/env-private-dynamic/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/env-private-service-worker/tsconfig.json b/packages/kit/test/build-errors/apps/env-private-service-worker/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/env-private-service-worker/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/env-private-service-worker/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/env-private/tsconfig.json b/packages/kit/test/build-errors/apps/env-private/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/env-private/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/env-private/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/prerender-entry-generator-mismatch/tsconfig.json b/packages/kit/test/build-errors/apps/prerender-entry-generator-mismatch/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/build-errors/apps/prerender-entry-generator-mismatch/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/prerender-entry-generator-mismatch/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/build-errors/apps/prerender-remote-function-error/tsconfig.json b/packages/kit/test/build-errors/apps/prerender-remote-function-error/tsconfig.json
index d1eb33103f2d..4ab2e9e5330d 100644
--- a/packages/kit/test/build-errors/apps/prerender-remote-function-error/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/prerender-remote-function-error/tsconfig.json
@@ -5,5 +5,5 @@
"noEmit": true,
"rewriteRelativeImportExtensions": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/build-errors/apps/prerenderable-incorrect-fragment/tsconfig.json b/packages/kit/test/build-errors/apps/prerenderable-incorrect-fragment/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/build-errors/apps/prerenderable-incorrect-fragment/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/prerenderable-incorrect-fragment/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/build-errors/apps/prerenderable-not-prerendered/tsconfig.json b/packages/kit/test/build-errors/apps/prerenderable-not-prerendered/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/build-errors/apps/prerenderable-not-prerendered/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/prerenderable-not-prerendered/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/build-errors/apps/remote-function-without-flag/tsconfig.json b/packages/kit/test/build-errors/apps/remote-function-without-flag/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/remote-function-without-flag/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/remote-function-without-flag/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/server-only-folder-dynamic-import/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-folder-dynamic-import/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/server-only-folder-dynamic-import/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/server-only-folder-dynamic-import/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/server-only-folder/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-folder/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/server-only-folder/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/server-only-folder/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/server-only-module-dynamic-import/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/server-only-module/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/server-only-shared/tsconfig.json b/packages/kit/test/build-errors/apps/server-only-shared/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/server-only-shared/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/server-only-shared/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/build-errors/apps/syntax-error/tsconfig.json b/packages/kit/test/build-errors/apps/syntax-error/tsconfig.json
index 8e767983eeb2..ff33d4b1df33 100644
--- a/packages/kit/test/build-errors/apps/syntax-error/tsconfig.json
+++ b/packages/kit/test/build-errors/apps/syntax-error/tsconfig.json
@@ -1,5 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
+ "extends": "$app/tsconfig",
"compilerOptions": {
"allowJs": true,
"checkJs": true,
diff --git a/packages/kit/test/prerendering/basics/tsconfig.json b/packages/kit/test/prerendering/basics/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/prerendering/basics/tsconfig.json
+++ b/packages/kit/test/prerendering/basics/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/prerendering/options/tsconfig.json b/packages/kit/test/prerendering/options/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/prerendering/options/tsconfig.json
+++ b/packages/kit/test/prerendering/options/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/test/prerendering/paths-base/tsconfig.json b/packages/kit/test/prerendering/paths-base/tsconfig.json
index b1096bf168cd..a14103c5fc1b 100644
--- a/packages/kit/test/prerendering/paths-base/tsconfig.json
+++ b/packages/kit/test/prerendering/paths-base/tsconfig.json
@@ -4,5 +4,5 @@
"checkJs": true,
"noEmit": true
},
- "extends": "./.svelte-kit/tsconfig.json"
+ "extends": "$app/tsconfig"
}
diff --git a/packages/kit/tsconfig.json b/packages/kit/tsconfig.json
index 86b0ce7f2340..d859c2ba6ace 100644
--- a/packages/kit/tsconfig.json
+++ b/packages/kit/tsconfig.json
@@ -27,5 +27,5 @@
"types": ["node"]
},
"include": ["*.js", "scripts/**/*", "src/**/*"],
- "exclude": ["./**/write_types/test/**"]
+ "exclude": ["./**/write_types/test/**", "./src/runtime/app/service-worker"]
}
diff --git a/packages/kit/types/index.d.ts b/packages/kit/types/index.d.ts
index f3c28f49ed09..35bc4f2f2d8b 100644
--- a/packages/kit/types/index.d.ts
+++ b/packages/kit/types/index.d.ts
@@ -833,6 +833,9 @@ declare module '@sveltejs/kit' {
*/
server?: boolean;
};
+ /**
+ * @deprecated Add configuration to `tsconfig.json` directly
+ */
typescript?: {
/**
* A function that allows you to edit the generated `tsconfig.json`. You can mutate the config (recommended) or return a new one.
@@ -3568,6 +3571,19 @@ declare module '$app/server' {
export {};
}
+declare module '$app/service-worker' {
+ /**
+ * The execution context of a service worker. This export exists to make it easier to
+ * use service workers with the correct types, provided the importing module is governed
+ * by a `tsconfig.json` that extends [`$app/tsconfig/service-worker`](https://svelte.dev/docs/kit/$app-tsconfig-service-worker).
+ *
+ */
+ // @ts-ignore
+ export const self: ServiceWorkerGlobalScope;
+
+ export {};
+}
+
declare module '$app/state' {
/**
* A read-only reactive object with information about the current page, serving several use cases:
diff --git a/playgrounds/basic/package.json b/playgrounds/basic/package.json
index 44d2dc2580d6..e39489596765 100644
--- a/playgrounds/basic/package.json
+++ b/playgrounds/basic/package.json
@@ -48,5 +48,8 @@
"!dist/**/*.spec.*"
],
"svelte": "./dist/index.js",
- "types": "./dist/index.d.ts"
+ "types": "./dist/index.d.ts",
+ "imports": {
+ "#lib/*": "./src/lib/*"
+ }
}
diff --git a/playgrounds/basic/src/service-worker/index.ts b/playgrounds/basic/src/service-worker/index.ts
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/playgrounds/basic/src/service-worker/tsconfig.json b/playgrounds/basic/src/service-worker/tsconfig.json
new file mode 100644
index 000000000000..a9db6421edca
--- /dev/null
+++ b/playgrounds/basic/src/service-worker/tsconfig.json
@@ -0,0 +1,3 @@
+{
+ "extends": "$app/tsconfig/service-worker"
+}
diff --git a/playgrounds/basic/tsconfig.json b/playgrounds/basic/tsconfig.json
index 9abacf4748b1..a17ce35075a3 100644
--- a/playgrounds/basic/tsconfig.json
+++ b/playgrounds/basic/tsconfig.json
@@ -1,16 +1,5 @@
{
- "extends": "./.svelte-kit/tsconfig.json",
- "compilerOptions": {
- "allowJs": true,
- "checkJs": true,
- "esModuleInterop": true,
- "forceConsistentCasingInFileNames": true,
- "resolveJsonModule": true,
- "skipLibCheck": true,
- "sourceMap": true
- }
- // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias and https://svelte.dev/docs/kit/configuration#files
- //
- // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
- // from the referenced tsconfig.json - TypeScript does not merge them in
+ "extends": "$app/tsconfig",
+ "include": ["src"],
+ "exclude": ["src/service-worker"]
}