// @ts-check /** This script modifies the project to support TS code in .svelte files like: <script lang="ts"> export let name: string; </script> As well as validating the code for CI. */ /** To work on this script: rm -rf test-template template && git clone sveltejs/template test-template && node scripts/setupTypeScript.js test-template */ const fs = require("fs") const path = require("path") const { argv } = require("process") const projectRoot = argv[2] || path.join(__dirname, "..") // Add deps to pkg.json const packageJSON = JSON.parse(fs.readFileSync(path.join(projectRoot, "package.json"), "utf8")) packageJSON.devDependencies = Object.assign(packageJSON.devDependencies, { "svelte-check": "^1.0.0", "svelte-preprocess": "^4.0.0", "@rollup/plugin-typescript": "^6.0.0", "typescript": "^3.9.3", "tslib": "^2.0.0", "@tsconfig/svelte": "^1.0.0" }) // Add script for checking packageJSON.scripts = Object.assign(packageJSON.scripts, { "validate": "svelte-check" }) // Write the package JSON fs.writeFileSync(path.join(projectRoot, "package.json"), JSON.stringify(packageJSON, null, " ")) // mv src/main.js to main.ts - note, we need to edit rollup.config.js for this too const beforeMainJSPath = path.join(projectRoot, "src", "main.js") const afterMainTSPath = path.join(projectRoot, "src", "main.ts") fs.renameSync(beforeMainJSPath, afterMainTSPath) // Switch the app.svelte file to use TS const appSveltePath = path.join(projectRoot, "src", "App.svelte") let appFile = fs.readFileSync(appSveltePath, "utf8") appFile = appFile.replace("<script>", '<script lang="ts">') appFile = appFile.replace("export let name;", 'export let name: string;') fs.writeFileSync(appSveltePath, appFile) // Edit rollup config const rollupConfigPath = path.join(projectRoot, "rollup.config.js") let rollupConfig = fs.readFileSync(rollupConfigPath, "utf8") // Edit imports rollupConfig = rollupConfig.replace(`'rollup-plugin-terser';`, `'rollup-plugin-terser'; import sveltePreprocess from 'svelte-preprocess'; import typescript from '@rollup/plugin-typescript';`) // Replace name of entry point rollupConfig = rollupConfig.replace(`'src/main.js'`, `'src/main.ts'`) // Add preprocess to the svelte config, this is tricky because there's no easy signifier. // Instead we look for `css:` then the next `}` and add the preprocessor to that let foundCSS = false let match // https://regex101.com/r/OtNjwo/1 const configEditor = new RegExp(/css:.|\n*}/gmi) while (( match = configEditor.exec(rollupConfig)) != null) { if (foundCSS) { const endOfCSSIndex = match.index + 1 rollupConfig = rollupConfig.slice(0, endOfCSSIndex) + ",\n preprocess: sveltePreprocess()," + rollupConfig.slice(endOfCSSIndex); break } if (match[0].includes("css:")) foundCSS = true } // Add TypeScript rollupConfig = rollupConfig.replace( 'commonjs(),', 'commonjs(),\n\t\ttypescript({\n\t\t\tsourceMap: !production,\n\t\t\tinlineSources: !production\n\t\t}),' ); fs.writeFileSync(rollupConfigPath, rollupConfig) // Add TSConfig const tsconfig = `{ "extends": "@tsconfig/svelte/tsconfig.json", "include": ["src/**/*"], "exclude": ["node_modules/*", "__sapper__/*", "public/*"] }` const tsconfigPath = path.join(projectRoot, "tsconfig.json") fs.writeFileSync(tsconfigPath, tsconfig) // Delete this script, but not during testing if (!argv[2]) { // Remove the script fs.unlinkSync(path.join(__filename)) // Check for Mac's DS_store file, and if it's the only one left remove it const remainingFiles = fs.readdirSync(path.join(__dirname)) if (remainingFiles.length === 1 && remainingFiles[0] === '.DS_store') { fs.unlinkSync(path.join(__dirname, '.DS_store')) } // Check if the scripts folder is empty if (fs.readdirSync(path.join(__dirname)).length === 0) { // Remove the scripts folder fs.rmdirSync(path.join(__dirname)) } } // Adds the extension recommendation fs.mkdirSync(path.join(projectRoot, ".vscode")) fs.writeFileSync(path.join(projectRoot, ".vscode", "extensions.json"), `{ "recommendations": ["svelte.svelte-vscode"] } `) console.log("Converted to TypeScript.") if (fs.existsSync(path.join(projectRoot, "node_modules"))) { console.log("\nYou will need to re-run your dependency manager to get started.") }