import fs__default from 'fs'; import path__default from 'path'; import { $ } from './index.js'; import { m as mkdirp, p as posixify } from './filesystem.js'; /** @type {Map} */ const previous_contents = new Map(); /** * @param {string} file * @param {string} code */ function write_if_changed(file, code) { if (code !== previous_contents.get(file)) { write(file, code); } } /** * @param {string} file * @param {string} code */ function write(file, code) { previous_contents.set(file, code); mkdirp(path__default.dirname(file)); fs__default.writeFileSync(file, code); } /** @param {string} str */ function trim(str) { const indentation = /** @type {RegExpExecArray} */ (/\n?(\s*)/.exec(str))[1]; const pattern = new RegExp(`^${indentation}`, 'gm'); return str.replace(pattern, '').trim(); } const reserved = new Set([ 'do', 'if', 'in', 'for', 'let', 'new', 'try', 'var', 'case', 'else', 'enum', 'eval', 'null', 'this', 'true', 'void', 'with', 'await', 'break', 'catch', 'class', 'const', 'false', 'super', 'throw', 'while', 'yield', 'delete', 'export', 'import', 'public', 'return', 'static', 'switch', 'typeof', 'default', 'extends', 'finally', 'package', 'private', 'continue', 'debugger', 'function', 'arguments', 'interface', 'protected', 'implements', 'instanceof' ]); const valid_identifier = /^[a-zA-Z_$][a-zA-Z0-9_$]*$/; /** * @param {string} cwd * @param {string} file */ function maybe_file(cwd, file) { const resolved = path__default.resolve(cwd, file); if (fs__default.existsSync(resolved)) { return resolved; } } /** * @param {string} file */ function project_relative(file) { return posixify(path__default.relative('.', file)); } /** * @param {string} file */ function remove_trailing_slashstar(file) { if (file.endsWith('/*')) { return file.slice(0, -2); } else { return file; } } /** * Writes the tsconfig that the user's tsconfig inherits from. * @param {import('types').ValidatedKitConfig} config */ function write_tsconfig(config, cwd = process.cwd()) { const out = path__default.join(config.outDir, 'tsconfig.json'); const user_file = maybe_file(cwd, 'tsconfig.json') || maybe_file(cwd, 'jsconfig.json'); if (user_file) validate(config, cwd, out, user_file); /** @param {string} file */ const config_relative = (file) => posixify(path__default.relative(config.outDir, file)); const include = ['ambient.d.ts']; for (const dir of [config.files.routes, config.files.lib]) { const relative = project_relative(path__default.dirname(dir)); include.push(config_relative(`${relative}/**/*.js`)); include.push(config_relative(`${relative}/**/*.ts`)); include.push(config_relative(`${relative}/**/*.svelte`)); } write_if_changed( out, JSON.stringify( { compilerOptions: { // generated options baseUrl: config_relative('.'), paths: get_tsconfig_paths(config), 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. importsNotUsedAsValues: 'error', // Vite compiles modules one at a time isolatedModules: true, // 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, // This is required for svelte-kit package to work as expected // Can be overwritten lib: ['esnext', 'DOM', 'DOM.Iterable'], moduleResolution: 'node', module: 'esnext', target: 'esnext' }, include, exclude: [config_relative('node_modules/**'), './[!ambient.d.ts]**'] }, null, '\t' ) ); } /** * @param {import('types').ValidatedKitConfig} config * @param {string} cwd * @param {string} out * @param {string} user_file */ function validate(config, cwd, out, user_file) { // we have to eval the file, since it's not parseable as JSON (contains comments) const user_tsconfig_json = fs__default.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__default.resolve(cwd, extend) === out; const kind = path__default.basename(user_file); if (extends_framework_config) { const { paths: user_paths } = user_tsconfig.compilerOptions || {}; if (user_paths && fs__default.existsSync(config.files.lib)) { /** @type {string[]} */ const lib = user_paths['$lib'] || []; /** @type {string[]} */ const lib_ = user_paths['$lib/*'] || []; const missing_lib_paths = !lib.some((relative) => path__default.resolve(cwd, relative) === config.files.lib) || !lib_.some((relative) => path__default.resolve(cwd, relative) === path__default.join(config.files.lib, '/*')); if (missing_lib_paths) { console.warn( $ .bold() .yellow(`Your compilerOptions.paths in ${kind} should include the following:`) ); const relative = posixify(path__default.relative('.', config.files.lib)); console.warn(`{\n "$lib":["${relative}"],\n "$lib/*":["${relative}/*"]\n}`); } } } else { let relative = posixify(path__default.relative('.', out)); if (!relative.startsWith('./')) relative = './' + relative; console.warn( $.bold().yellow(`Your ${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. * Related to vite alias creation. * * @param {import('types').ValidatedKitConfig} config */ function get_tsconfig_paths(config) { const alias = { ...config.alias }; if (fs__default.existsSync(project_relative(config.files.lib))) { alias['$lib'] = project_relative(config.files.lib); } /** @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 = project_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; } export { write as a, write_tsconfig as b, reserved as r, trim as t, valid_identifier as v, write_if_changed as w };