From d8341635d7834865f31107b57b8eff80549fa588 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 24 Feb 2022 16:27:53 -0500 Subject: [PATCH 01/10] generated tsconfig.json --- .changeset/silver-horses-check.md | 6 ++ .../shared/+typescript/tsconfig.json | 35 +------ .../shared/-typescript/jsconfig.json | 9 +- packages/kit/src/core/build/index.js | 3 + packages/kit/src/core/dev/index.js | 3 + packages/kit/src/core/tsconfig.js | 99 +++++++++++++++++++ 6 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 .changeset/silver-horses-check.md create mode 100644 packages/kit/src/core/tsconfig.js diff --git a/.changeset/silver-horses-check.md b/.changeset/silver-horses-check.md new file mode 100644 index 000000000000..072bf68587a0 --- /dev/null +++ b/.changeset/silver-horses-check.md @@ -0,0 +1,6 @@ +--- +'create-svelte': patch +'@sveltejs/kit': patch +--- + +Extend user tsconfig from generated .svelte-kit/tsconfig.json diff --git a/packages/create-svelte/shared/+typescript/tsconfig.json b/packages/create-svelte/shared/+typescript/tsconfig.json index 7b5a0dc0d400..81ff9770cd8a 100644 --- a/packages/create-svelte/shared/+typescript/tsconfig.json +++ b/packages/create-svelte/shared/+typescript/tsconfig.json @@ -1,36 +1,3 @@ { - "compilerOptions": { - "moduleResolution": "node", - "module": "es2020", - "lib": ["es2020", "DOM"], - "target": "es2020", - /** - 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. - */ - "importsNotUsedAsValues": "error", - /** - 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. Requires TS 4.5 or higher. - */ - "preserveValueImports": true, - "isolatedModules": true, - "resolveJsonModule": true, - /** - To have warnings/errors of the Svelte compiler at the correct position, - enable source maps by default. - */ - "sourceMap": true, - "esModuleInterop": true, - "skipLibCheck": true, - "forceConsistentCasingInFileNames": true, - "baseUrl": ".", - "allowJs": true, - "checkJs": true, - "paths": { - "$lib": ["src/lib"], - "$lib/*": ["src/lib/*"] - } - }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.ts", "src/**/*.svelte"] + "extends": "./.svelte-kit/tsconfig.json" } diff --git a/packages/create-svelte/shared/-typescript/jsconfig.json b/packages/create-svelte/shared/-typescript/jsconfig.json index 3757b0e28f6f..81ff9770cd8a 100644 --- a/packages/create-svelte/shared/-typescript/jsconfig.json +++ b/packages/create-svelte/shared/-typescript/jsconfig.json @@ -1,10 +1,3 @@ { - "compilerOptions": { - "baseUrl": ".", - "paths": { - "$lib": ["src/lib"], - "$lib/*": ["src/lib/*"] - } - }, - "include": ["src/**/*.d.ts", "src/**/*.js", "src/**/*.svelte"] + "extends": "./.svelte-kit/tsconfig.json" } diff --git a/packages/kit/src/core/build/index.js b/packages/kit/src/core/build/index.js index 99606d80611b..3fb90bc51f70 100644 --- a/packages/kit/src/core/build/index.js +++ b/packages/kit/src/core/build/index.js @@ -8,6 +8,7 @@ import { generate_manifest } from '../generate_manifest/index.js'; import { build_service_worker } from './build_service_worker.js'; import { build_client } from './build_client.js'; import { build_server } from './build_server.js'; +import { generate_tsconfig } from '../tsconfig.js'; /** * @param {import('types').ValidatedConfig} config @@ -24,6 +25,8 @@ export async function build(config) { rimraf(output_dir); mkdirp(output_dir); + generate_tsconfig(config); + const options = { cwd, config, diff --git a/packages/kit/src/core/dev/index.js b/packages/kit/src/core/dev/index.js index f4c4f0673119..f411b90a992e 100644 --- a/packages/kit/src/core/dev/index.js +++ b/packages/kit/src/core/dev/index.js @@ -6,6 +6,7 @@ import { print_config_conflicts } from '../config/index.js'; import { SVELTE_KIT } from '../constants.js'; import { copy_assets, get_aliases, runtime } from '../utils.js'; import { create_plugin } from './plugin.js'; +import { generate_tsconfig } from '../tsconfig.js'; /** * @typedef {{ @@ -22,6 +23,8 @@ import { create_plugin } from './plugin.js'; export async function dev({ cwd, port, host, https, config }) { copy_assets(`${SVELTE_KIT}/runtime`); + generate_tsconfig(config); + const [vite_config] = deep_merge( { server: { diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js new file mode 100644 index 000000000000..22d8fd86fb14 --- /dev/null +++ b/packages/kit/src/core/tsconfig.js @@ -0,0 +1,99 @@ +import fs from 'fs'; +import path from 'path'; +import { mkdirp } from '../utils/filesystem.js'; +import { SVELTE_KIT } from './constants.js'; + +/** @param {string} file */ +const exists = (file) => fs.existsSync(file) && file; + +/** @param {import('types').ValidatedConfig} config */ +export function generate_tsconfig(config) { + const out = path.resolve(SVELTE_KIT, 'tsconfig.json'); + + const user_file = exists('tsconfig.json') || exists('jsconfig.json'); + + const paths = {}; + + paths['$lib'] = [path.relative(SVELTE_KIT, config.kit.files.lib)]; + paths['$lib/*'] = [path.relative(SVELTE_KIT, config.kit.files.lib) + '/*']; + + if (user_file) { + const user_tsconfig = JSON.parse(fs.readFileSync(user_file, 'utf-8')); + + // we need to check that the user's tsconfig extends the framework config + const extend = user_tsconfig.extends; + const extends_framework_config = extend && path.resolve('.', extend) === out; + + if (extends_framework_config) { + const { paths: user_paths } = user_tsconfig.compilerOptions || {}; + + if (user_paths) { + const lib = user_paths['$lib'] || []; + const lib_ = user_paths['$lib/*'] || []; + + const missing_lib_paths = + !lib.some( + (/** @type {string} */ relative) => path.resolve('.', relative) === config.kit.files.lib + ) || + !lib_.some( + (/** @type {string} */ relative) => + path.resolve('.', relative) === config.kit.files.lib + '/*' + ); + + if (missing_lib_paths) { + console.warn( + `\u001B[1m\u001B[93mYour compilerOptions.paths in ${user_file} should include the following:\u001B[39m\u001B[22m` + ); + const relative = path.relative('.', config.kit.files.lib); + console.warn(`{\n "$lib":["${relative}"],\n "$lib/*":["${relative}/*"]\n}`); + } + } + } else { + let relative = path.relative('.', out); + if (relative.startsWith(SVELTE_KIT)) relative = './' + relative; + + console.warn( + `\u001B[1m\u001B[93mYour ${user_file} should include the following:\u001B[39m\u001B[22m` + ); + console.warn(`"extends": "${relative}"`); + } + } + + mkdirp(SVELTE_KIT); + + fs.writeFileSync( + `${SVELTE_KIT}/tsconfig.json`, + JSON.stringify( + { + compilerOptions: { + moduleResolution: 'node', + module: 'es2020', + lib: ['es2020', 'DOM'], + target: 'es2020', + // 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. + importsNotUsedAsValues: 'error', + // 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. Requires TS 4.5 or higher. + preserveValueImports: true, + isolatedModules: true, + resolveJsonModule: true, + // To have warnings/errors of the Svelte compiler at the correct position, + // enable source maps by default. + sourceMap: true, + esModuleInterop: true, + skipLibCheck: true, + forceConsistentCasingInFileNames: true, + baseUrl: path.relative(SVELTE_KIT, '.'), + allowJs: true, + checkJs: true, + paths + }, + include: ['../**/*.d.ts', '../**/*.js', '../**/*.ts', '../**/*.svelte'], + exclude: ['../node_modules/**'] + }, + null, + '\t' + ) + ); +} From 8d20d50193a1fa17cad92cb40eb04ad80ebd4c38 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Thu, 24 Feb 2022 16:38:51 -0500 Subject: [PATCH 02/10] eval user config --- packages/kit/src/core/tsconfig.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 22d8fd86fb14..98335c758b59 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -18,7 +18,9 @@ export function generate_tsconfig(config) { paths['$lib/*'] = [path.relative(SVELTE_KIT, config.kit.files.lib) + '/*']; if (user_file) { - const user_tsconfig = JSON.parse(fs.readFileSync(user_file, 'utf-8')); + // we have to eval the file, since it's not parseable as JSON (contains comments) + const user_tsconfig_json = fs.readFileSync(user_file, 'utf-8'); + const user_tsconfig = (0, eval)(`(${user_tsconfig_json})`); // we need to check that the user's tsconfig extends the framework config const extend = user_tsconfig.extends; From a79536dcb57f03ed3678a3dba79942cabeff27ef Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:40:26 -0500 Subject: [PATCH 03/10] Update packages/kit/src/core/tsconfig.js Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- packages/kit/src/core/tsconfig.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 98335c758b59..ef5eee2ff1c7 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -12,10 +12,10 @@ export function generate_tsconfig(config) { const user_file = exists('tsconfig.json') || exists('jsconfig.json'); - const paths = {}; - - paths['$lib'] = [path.relative(SVELTE_KIT, config.kit.files.lib)]; - paths['$lib/*'] = [path.relative(SVELTE_KIT, config.kit.files.lib) + '/*']; + const paths = { + '$lib': [path.relative(SVELTE_KIT, config.kit.files.lib)], + '$lib/*': [path.relative(SVELTE_KIT, config.kit.files.lib) + '/*']; + }; if (user_file) { // we have to eval the file, since it's not parseable as JSON (contains comments) From 0271b1d9f8a491bf6953be69862b8623ac454c93 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:41:11 -0500 Subject: [PATCH 04/10] use kleur instead of magic ansi codes --- packages/kit/src/core/tsconfig.js | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 98335c758b59..7cd4f4a7b2be 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -1,5 +1,6 @@ import fs from 'fs'; import path from 'path'; +import colors from 'kleur'; import { mkdirp } from '../utils/filesystem.js'; import { SVELTE_KIT } from './constants.js'; @@ -44,7 +45,9 @@ export function generate_tsconfig(config) { if (missing_lib_paths) { console.warn( - `\u001B[1m\u001B[93mYour compilerOptions.paths in ${user_file} should include the following:\u001B[39m\u001B[22m` + colors + .bold() + .yellow(`Your compilerOptions.paths in ${user_file} should include the following:`) ); const relative = path.relative('.', config.kit.files.lib); console.warn(`{\n "$lib":["${relative}"],\n "$lib/*":["${relative}/*"]\n}`); @@ -55,9 +58,11 @@ export function generate_tsconfig(config) { if (relative.startsWith(SVELTE_KIT)) relative = './' + relative; console.warn( - `\u001B[1m\u001B[93mYour ${user_file} should include the following:\u001B[39m\u001B[22m` + colors + .bold() + .yellow(`Your ${user_file} should extend the configuration generated by SvelteKit:`) ); - console.warn(`"extends": "${relative}"`); + console.warn(`{\n "extends": "${relative}"\n}`); } } From ebe5920cfac557eb1f667f07ba92a63f9eb3301b Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:41:28 -0500 Subject: [PATCH 05/10] use base-relative URLs --- packages/kit/src/core/tsconfig.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 7cd4f4a7b2be..f079bf768e17 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -96,8 +96,7 @@ export function generate_tsconfig(config) { checkJs: true, paths }, - include: ['../**/*.d.ts', '../**/*.js', '../**/*.ts', '../**/*.svelte'], - exclude: ['../node_modules/**'] + include: ['src/**/*.d.ts', 'src/**/*.js', 'src/**/*.ts', 'src/**/*.svelte'] }, null, '\t' From 53ec87e2680f13a0509f439f78b007b67740c386 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:44:30 -0500 Subject: [PATCH 06/10] paths should be relative to baseURL --- packages/kit/src/core/tsconfig.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 77a0558df6b5..39b528d0cf49 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -14,8 +14,8 @@ export function generate_tsconfig(config) { const user_file = exists('tsconfig.json') || exists('jsconfig.json'); const paths = { - '$lib': [path.relative(SVELTE_KIT, config.kit.files.lib)], - '$lib/*': [path.relative(SVELTE_KIT, config.kit.files.lib) + '/*']; + $lib: [path.relative('.', config.kit.files.lib)], + '$lib/*': [path.relative('.', config.kit.files.lib) + '/*'] }; if (user_file) { From 0fe579cd202763c7bee1d07468630aa08c7a8cb5 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:44:43 -0500 Subject: [PATCH 07/10] include/exclude are NOT relative to baseURL --- packages/kit/src/core/tsconfig.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 39b528d0cf49..de6fb1a5d0c6 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -96,7 +96,8 @@ export function generate_tsconfig(config) { checkJs: true, paths }, - include: ['src/**/*.d.ts', 'src/**/*.js', 'src/**/*.ts', 'src/**/*.svelte'] + include: ['../**/*.d.ts', '../**/*.js', '../**/*.ts', '../**/*.svelte'], + exclude: ['../node_modules/**'] }, null, '\t' From f77c07541f84f2333bc452337ac0ceacdd3bc9f3 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:46:09 -0500 Subject: [PATCH 08/10] tidy up --- packages/kit/src/core/tsconfig.js | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index de6fb1a5d0c6..37e2edf99d39 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -10,14 +10,8 @@ const exists = (file) => fs.existsSync(file) && file; /** @param {import('types').ValidatedConfig} config */ export function generate_tsconfig(config) { const out = path.resolve(SVELTE_KIT, 'tsconfig.json'); - const user_file = exists('tsconfig.json') || exists('jsconfig.json'); - const paths = { - $lib: [path.relative('.', config.kit.files.lib)], - '$lib/*': [path.relative('.', config.kit.files.lib) + '/*'] - }; - if (user_file) { // we have to eval the file, since it's not parseable as JSON (contains comments) const user_tsconfig_json = fs.readFileSync(user_file, 'utf-8'); @@ -94,7 +88,10 @@ export function generate_tsconfig(config) { baseUrl: path.relative(SVELTE_KIT, '.'), allowJs: true, checkJs: true, - paths + paths: { + $lib: [path.relative('.', config.kit.files.lib)], + '$lib/*': [path.relative('.', config.kit.files.lib) + '/*'] + } }, include: ['../**/*.d.ts', '../**/*.js', '../**/*.ts', '../**/*.svelte'], exclude: ['../node_modules/**'] From 03e8ff012d23c446137e51a38d7bd39d96aada63 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 12:59:38 -0500 Subject: [PATCH 09/10] tidy up, relativise paths consistently --- packages/kit/src/core/tsconfig.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 37e2edf99d39..95628c191efc 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -62,6 +62,12 @@ export function generate_tsconfig(config) { mkdirp(SVELTE_KIT); + /** @param {string} file */ + const project_relative = (file) => path.relative('.', file); + + /** @param {string} file */ + const config_relative = (file) => path.relative(SVELTE_KIT, file); + fs.writeFileSync( `${SVELTE_KIT}/tsconfig.json`, JSON.stringify( @@ -85,16 +91,16 @@ export function generate_tsconfig(config) { esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, - baseUrl: path.relative(SVELTE_KIT, '.'), + baseUrl: config_relative('.'), allowJs: true, checkJs: true, paths: { - $lib: [path.relative('.', config.kit.files.lib)], - '$lib/*': [path.relative('.', config.kit.files.lib) + '/*'] + $lib: [project_relative(config.kit.files.lib)], + '$lib/*': [project_relative(config.kit.files.lib + '/*')] } }, - include: ['../**/*.d.ts', '../**/*.js', '../**/*.ts', '../**/*.svelte'], - exclude: ['../node_modules/**'] + include: ['**/*.d.ts', '**/*.js', '**/*.ts', '**/*.svelte'].map(config_relative), + exclude: ['node_modules/**'].map(config_relative) }, null, '\t' From dd83df18678070caa1dd4e8aeb2a2eadd5f4a3a6 Mon Sep 17 00:00:00 2001 From: Rich Harris Date: Mon, 28 Feb 2022 13:04:03 -0500 Subject: [PATCH 10/10] move validation into separate function --- packages/kit/src/core/tsconfig.js | 101 ++++++++++++++++-------------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/packages/kit/src/core/tsconfig.js b/packages/kit/src/core/tsconfig.js index 95628c191efc..a5909cfc6a35 100644 --- a/packages/kit/src/core/tsconfig.js +++ b/packages/kit/src/core/tsconfig.js @@ -12,53 +12,7 @@ export function generate_tsconfig(config) { const out = path.resolve(SVELTE_KIT, 'tsconfig.json'); const user_file = exists('tsconfig.json') || exists('jsconfig.json'); - if (user_file) { - // we have to eval the file, since it's not parseable as JSON (contains comments) - const user_tsconfig_json = fs.readFileSync(user_file, 'utf-8'); - const user_tsconfig = (0, eval)(`(${user_tsconfig_json})`); - - // we need to check that the user's tsconfig extends the framework config - const extend = user_tsconfig.extends; - const extends_framework_config = extend && path.resolve('.', extend) === out; - - if (extends_framework_config) { - const { paths: user_paths } = user_tsconfig.compilerOptions || {}; - - if (user_paths) { - const lib = user_paths['$lib'] || []; - const lib_ = user_paths['$lib/*'] || []; - - const missing_lib_paths = - !lib.some( - (/** @type {string} */ relative) => path.resolve('.', relative) === config.kit.files.lib - ) || - !lib_.some( - (/** @type {string} */ relative) => - path.resolve('.', relative) === config.kit.files.lib + '/*' - ); - - if (missing_lib_paths) { - console.warn( - colors - .bold() - .yellow(`Your compilerOptions.paths in ${user_file} should include the following:`) - ); - const relative = path.relative('.', config.kit.files.lib); - console.warn(`{\n "$lib":["${relative}"],\n "$lib/*":["${relative}/*"]\n}`); - } - } - } else { - let relative = path.relative('.', out); - if (relative.startsWith(SVELTE_KIT)) relative = './' + relative; - - console.warn( - colors - .bold() - .yellow(`Your ${user_file} should extend the configuration generated by SvelteKit:`) - ); - console.warn(`{\n "extends": "${relative}"\n}`); - } - } + if (user_file) validate(config, out, user_file); mkdirp(SVELTE_KIT); @@ -107,3 +61,56 @@ export function generate_tsconfig(config) { ) ); } + +/** + * @param {import('types').ValidatedConfig} config + * @param {string} out + * @param {string} user_file + */ +function validate(config, out, user_file) { + // we have to eval the file, since it's not parseable as JSON (contains comments) + const user_tsconfig_json = fs.readFileSync(user_file, 'utf-8'); + const user_tsconfig = (0, eval)(`(${user_tsconfig_json})`); + + // we need to check that the user's tsconfig extends the framework config + const extend = user_tsconfig.extends; + const extends_framework_config = extend && path.resolve('.', extend) === out; + + if (extends_framework_config) { + const { paths: user_paths } = user_tsconfig.compilerOptions || {}; + + if (user_paths) { + const lib = user_paths['$lib'] || []; + const lib_ = user_paths['$lib/*'] || []; + + const missing_lib_paths = + !lib.some( + (/** @type {string} */ relative) => path.resolve('.', relative) === config.kit.files.lib + ) || + !lib_.some( + (/** @type {string} */ relative) => + path.resolve('.', relative) === config.kit.files.lib + '/*' + ); + + if (missing_lib_paths) { + console.warn( + colors + .bold() + .yellow(`Your compilerOptions.paths in ${user_file} should include the following:`) + ); + const relative = path.relative('.', config.kit.files.lib); + console.warn(`{\n "$lib":["${relative}"],\n "$lib/*":["${relative}/*"]\n}`); + } + } + } else { + let relative = path.relative('.', out); + if (relative.startsWith(SVELTE_KIT)) relative = './' + relative; + + console.warn( + colors + .bold() + .yellow(`Your ${user_file} should extend the configuration generated by SvelteKit:`) + ); + console.warn(`{\n "extends": "${relative}"\n}`); + } +}