Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

breaking: remove baseUrl fallback from generated tsconfig #11294

Merged
merged 1 commit into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/dull-eyes-check.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@sveltejs/kit": major
---

breaking: remove baseUrl fallback from generated tsconfig
5 changes: 5 additions & 0 deletions .changeset/loud-parrots-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"svelte-migrate": minor
---

feat: add sveltekit v2 migration
4 changes: 4 additions & 0 deletions documentation/docs/60-appendix/30-migrating-to-sveltekit-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,10 @@ In SvelteKit 1, those properties included `form` and `data`. These were deprecat

If a form contains an `<input type="file">` but does not have an `enctype="multipart/form-data"` attribute, non-JS submissions will omit the file. SvelteKit 2 will throw an error if it encounters a form like this during a `use:enhance` submission to ensure that your forms work correctly when JavaScript is not present.

## Generated `tsconfig.json` is more strict

Previously, the generated `tsconfig.json` was trying its best to still produce a somewhat valid config when your `tsconfig.json` included `paths` or `baseUrl`. In SvelteKit 2, the validation is more strict and will warn when you use either `paths` or `baseUrl` in your `tsconfig.json`. These settings are used to generate path aliases and you should use [the `alias` config](configuration#alias) option in your `svelte.config.js` instead, to also create a corresponding alias for the bundler.

## Updated dependency requirements

SvelteKit 2 requires Node `18.13` or higher, and the following minimum dependency versions:
Expand Down
83 changes: 18 additions & 65 deletions packages/kit/src/core/sync/write_tsconfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,46 +41,16 @@ export function write_tsconfig(kit, cwd = process.cwd()) {
const out = path.join(kit.outDir, 'tsconfig.json');

const user_config = load_user_tsconfig(cwd);
if (user_config) validate_user_config(kit, cwd, out, user_config);

// only specify baseUrl if a) the user doesn't specify their own baseUrl
// and b) they have non-relative paths. this causes problems with auto-imports,
// so we print a suggestion that they use relative paths instead
// TODO(v2): never include base URL, and skip the check below
let include_base_url = false;

if (user_config && !user_config.options.compilerOptions?.baseUrl) {
const non_relative_paths = new Set();
for (const paths of Object.values(user_config?.options.compilerOptions?.paths || {})) {
for (const path of paths) {
if (!path.startsWith('.')) non_relative_paths.add(path);
}
}

if (non_relative_paths.size) {
include_base_url = true;

console.log(colors.bold().yellow('Please replace non-relative compilerOptions.paths:\n'));

for (const path of non_relative_paths) {
console.log(` - "${path}" -> "./${path}"`);
}

console.log(
'\nDoing so allows us to omit "baseUrl" — which causes problems with imports — from the generated tsconfig.json. See https://github.com/sveltejs/kit/pull/8437 for more information.'
);
}
}
if (user_config) validate_user_config(cwd, out, user_config);

write_if_changed(out, JSON.stringify(get_tsconfig(kit, include_base_url), null, '\t'));
write_if_changed(out, JSON.stringify(get_tsconfig(kit), null, '\t'));
}

/**
* Generates the tsconfig that the user's tsconfig inherits from.
* @param {import('types').ValidatedKitConfig} kit
* @param {boolean} include_base_url
*/
export function get_tsconfig(kit, include_base_url) {
export function get_tsconfig(kit) {
/** @param {string} file */
const config_relative = (file) => posixify(path.relative(kit.outDir, file));

Expand Down Expand Up @@ -122,8 +92,7 @@ export function get_tsconfig(kit, include_base_url) {
const config = {
compilerOptions: {
// generated options
baseUrl: include_base_url ? config_relative('.') : undefined,
paths: get_tsconfig_paths(kit, include_base_url),
paths: get_tsconfig_paths(kit),
rootDirs: [config_relative('.'), './types'],

// essential options
Expand Down Expand Up @@ -166,12 +135,11 @@ function load_user_tsconfig(cwd) {
}

/**
* @param {import('types').ValidatedKitConfig} kit
* @param {string} cwd
* @param {string} out
* @param {{ kind: string, options: any }} config
*/
function validate_user_config(kit, cwd, out, 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 =
Expand All @@ -184,29 +152,17 @@ function validate_user_config(kit, cwd, out, config) {
const options = config.options.compilerOptions || {};

if (extends_framework_config) {
const { paths: user_paths } = options;

if (user_paths && fs.existsSync(kit.files.lib)) {
/** @type {string[]} */
const lib = user_paths['$lib'] || [];
/** @type {string[]} */
const lib_ = user_paths['$lib/*'] || [];

// TODO(v2): check needs to be adjusted when we remove the base path
const missing_lib_paths =
!lib.some((relative) => path.resolve(cwd, relative) === kit.files.lib) ||
!lib_.some((relative) => path.resolve(cwd, relative) === path.join(kit.files.lib, '/*'));

if (missing_lib_paths) {
console.warn(
colors
.bold()
.yellow(`Your compilerOptions.paths in ${config.kind} should include the following:`)
);
let relative = posixify(path.relative('.', kit.files.lib));
if (!relative.startsWith('.')) relative = `./${relative}`;
console.warn(`{\n "$lib":["${relative}"],\n "$lib/*":["${relative}/*"]\n}`);
}
const { paths, baseUrl } = options;

if (baseUrl || paths) {
console.warn(
colors
.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 `kit.alias` instead: https://kit.svelte.dev/docs/configuration#alias'
)
);
}
} else {
let relative = posixify(path.relative('.', out));
Expand All @@ -231,9 +187,8 @@ const value_regex = /^(.*?)((\/\*)|(\.\w+))?$/;
* Related to vite alias creation.
*
* @param {import('types').ValidatedKitConfig} config
* @param {boolean} include_base_url
*/
function get_tsconfig_paths(config, include_base_url) {
function get_tsconfig_paths(config) {
/** @param {string} file */
const config_relative = (file) => posixify(path.relative(config.outDir, file));

Expand All @@ -252,9 +207,7 @@ function get_tsconfig_paths(config, include_base_url) {
const value_match = value_regex.exec(value);
if (!value_match) throw new Error(`Invalid alias value: ${value}`);

const rel_path = (include_base_url ? project_relative : config_relative)(
remove_trailing_slashstar(value)
);
const rel_path = config_relative(remove_trailing_slashstar(value));
const slashstar = key_match[2];

if (slashstar) {
Expand Down
33 changes: 4 additions & 29 deletions packages/kit/src/core/sync/write_tsconfig.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ test('Creates tsconfig path aliases from kit.alias', () => {
}
});

const { compilerOptions } = get_tsconfig(kit, false);
const { compilerOptions } = get_tsconfig(kit);

// $lib isn't part of the outcome because there's a "path exists"
// check in the implementation
Expand All @@ -27,31 +27,6 @@ test('Creates tsconfig path aliases from kit.alias', () => {
});
});

test('Creates tsconfig path aliases from kit.alias with existing baseUrl', () => {
const { kit } = validate_config({
kit: {
alias: {
simpleKey: 'simple/value',
key: 'value',
'key/*': 'some/other/value/*',
keyToFile: 'path/to/file.ts'
}
}
});

const { compilerOptions } = get_tsconfig(kit, true);

// $lib isn't part of the outcome because there's a "path exists"
// check in the implementation
expect(compilerOptions.paths).toEqual({
simpleKey: ['simple/value'],
'simpleKey/*': ['simple/value/*'],
key: ['value'],
'key/*': ['some/other/value/*'],
keyToFile: ['path/to/file.ts']
});
});

test('Allows generated tsconfig to be mutated', () => {
const { kit } = validate_config({
kit: {
Expand All @@ -63,7 +38,7 @@ test('Allows generated tsconfig to be mutated', () => {
}
});

const config = get_tsconfig(kit, false);
const config = get_tsconfig(kit);

// @ts-expect-error
assert.equal(config.extends, 'some/other/tsconfig.json');
Expand All @@ -81,7 +56,7 @@ test('Allows generated tsconfig to be replaced', () => {
}
});

const config = get_tsconfig(kit, false);
const config = get_tsconfig(kit);

// @ts-expect-error
assert.equal(config.extends, 'some/other/tsconfig.json');
Expand All @@ -96,7 +71,7 @@ test('Creates tsconfig include from kit.files', () => {
}
});

const { include } = get_tsconfig(kit, false);
const { include } = get_tsconfig(kit);

expect(include).toEqual([
'ambient.d.ts',
Expand Down
6 changes: 6 additions & 0 deletions packages/migrate/migrations/sveltekit-2/migrate.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ export function update_tsconfig_content(content) {
);
}

if (content.includes('"paths":') || content.includes('"baseUrl":')) {
log_migration(
'`paths` and/or `baseUrl` detected in your tsconfig.json - remove it and use `kit.alias` instead: https://kit.svelte.dev/docs/v2-migration-guide#generated-tsconfigjson-is-more-strict'
);
}

return updated;
}

Expand Down
Loading