diff --git a/.changeset/cold-boats-wave.md b/.changeset/cold-boats-wave.md new file mode 100644 index 000000000000..dd2e65846602 --- /dev/null +++ b/.changeset/cold-boats-wave.md @@ -0,0 +1,5 @@ +--- +'create-svelte': patch +--- + +Disable type checking by default for non-typescript projects. diff --git a/packages/create-svelte/bin.js b/packages/create-svelte/bin.js index 8439db01fc31..0997c21c274b 100755 --- a/packages/create-svelte/bin.js +++ b/packages/create-svelte/bin.js @@ -70,12 +70,15 @@ async function main() { }) }, { - type: 'toggle', - name: 'typescript', - message: 'Use TypeScript?', + type: 'select', + name: 'types', + message: 'Add type checking?', initial: false, - active: 'Yes', - inactive: 'No' + choices: [ + { title: 'Type-checked JavaScript', value: 'checkjs' }, + { title: 'TypeScript', value: 'typescript' }, + { title: 'None', value: null } + ] }, { type: 'toggle', @@ -116,9 +119,12 @@ async function main() { console.log(bold(green('\nYour project is ready!'))); - if (options.typescript) { + if (options.types === 'typescript') { console.log(bold('✔ Typescript')); console.log(' Inside Svelte components, use `; + } + ); + + types.typescript.push({ + name, + contents: strip_jsdoc(contents) + }); + + types.checkjs.push({ + name, + contents: js_contents + }); + + types.null.push({ + name, + contents: strip_jsdoc(js_contents) + }); + } } else { const dest = path.join(assets, name.replace(/^\./, 'DOT-')); mkdirp(path.dirname(dest)); @@ -77,75 +162,13 @@ async function generate_templates(shared) { } }); - /** @type {import('../types/internal.js').File[]} */ - const js = []; - - for (const file of ts) { - // The app.d.ts file makes TS/JS aware of some ambient modules, which are - // also needed for JS projects if people turn on "checkJs" in their jsonfig - if (file.name.endsWith('.d.ts')) { - if (file.name.endsWith('app.d.ts')) js.push(file); - } else if (file.name.endsWith('.ts')) { - js.push({ - name: file.name.replace(/\.ts$/, '.js'), - contents: convert_typescript(file.contents) - }); - } else if (file.name.endsWith('.svelte')) { - // we jump through some hoops, rather than just using svelte.preprocess, - // so that the output preserves the original formatting to the extent - // possible (e.g. preserving double line breaks). Sucrase is the best - // tool for the job because it just removes the types; Prettier then - // tidies up the end result - const contents = file.contents.replace( - /]+)>([\s\S]+?)<\/script>/g, - (m, attrs, typescript) => { - // Sucrase assumes 'unused' imports (which _are_ used, but only - // in the markup) are type imports, and strips them. This step - // prevents it from drawing that conclusion - const imports = []; - const import_pattern = /import (.+?) from/g; - let import_match; - while ((import_match = import_pattern.exec(typescript))) { - const word_pattern = /[a-z_$][a-z0-9_$]*/gi; - let word_match; - while ((word_match = word_pattern.exec(import_match[1]))) { - imports.push(word_match[0]); - } - } - - const suffix = `\n${imports.join(',')}`; - - const transformed = transform(typescript + suffix, { - transforms: ['typescript'] - }).code.slice(0, -suffix.length); - - const contents = prettier - .format(transformed, { - parser: 'babel', - useTabs: true, - singleQuote: true, - trailingComma: 'none', - printWidth: 100 - }) - .trim() - .replace(/^(.)/gm, '\t$1'); - - return `\n${contents}\n`; - } - ); - - js.push({ - name: file.name, - contents - }); - } else { - js.push(file); - } - } - fs.copyFileSync(meta_file, `${dir}/meta.json`); - fs.writeFileSync(`${dir}/files.ts.json`, JSON.stringify(ts, null, '\t')); - fs.writeFileSync(`${dir}/files.js.json`, JSON.stringify(js, null, '\t')); + fs.writeFileSync( + `${dir}/files.types=typescript.json`, + JSON.stringify(types.typescript, null, '\t') + ); + fs.writeFileSync(`${dir}/files.types=checkjs.json`, JSON.stringify(types.checkjs, null, '\t')); + fs.writeFileSync(`${dir}/files.types=null.json`, JSON.stringify(types.null, null, '\t')); } } diff --git a/packages/create-svelte/scripts/update-template-repo-contents.js b/packages/create-svelte/scripts/update-template-repo-contents.js index 54c93b629a8e..6cd67e1c0b86 100644 --- a/packages/create-svelte/scripts/update-template-repo-contents.js +++ b/packages/create-svelte/scripts/update-template-repo-contents.js @@ -17,7 +17,7 @@ await create(repo, { name: 'kit-template-default', template: 'default', eslint: false, - typescript: false, + types: 'checkjs', prettier: true, playwright: false }); diff --git a/packages/create-svelte/shared/+checkjs/jsconfig.json b/packages/create-svelte/shared/+checkjs/jsconfig.json new file mode 100644 index 000000000000..df77605d0d91 --- /dev/null +++ b/packages/create-svelte/shared/+checkjs/jsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "checkJs": true + } +} diff --git a/packages/create-svelte/shared/+checkjs/package.json b/packages/create-svelte/shared/+checkjs/package.json new file mode 100644 index 000000000000..6d4d5148bf98 --- /dev/null +++ b/packages/create-svelte/shared/+checkjs/package.json @@ -0,0 +1,10 @@ +{ + "scripts": { + "check": "svelte-check --tsconfig ./jsconfig.json", + "check:watch": "svelte-check --tsconfig ./jsconfig.json --watch" + }, + "devDependencies": { + "typescript": "~4.6.2", + "svelte-check": "^2.2.6" + } +} diff --git a/packages/create-svelte/shared/+default+checkjs/package.json b/packages/create-svelte/shared/+default+checkjs/package.json new file mode 100644 index 000000000000..344e137f7c5a --- /dev/null +++ b/packages/create-svelte/shared/+default+checkjs/package.json @@ -0,0 +1,5 @@ +{ + "devDependencies": { + "@types/cookie": "^0.4.1" + } +} diff --git a/packages/create-svelte/shared/+default+checkjs/svelte.config.js b/packages/create-svelte/shared/+default+checkjs/svelte.config.js new file mode 100644 index 000000000000..1fa366b0c670 --- /dev/null +++ b/packages/create-svelte/shared/+default+checkjs/svelte.config.js @@ -0,0 +1,20 @@ +import adapter from '@sveltejs/adapter-auto'; +import preprocess from 'svelte-preprocess'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + // Consult https://github.com/sveltejs/svelte-preprocess + // for more information about preprocessors + preprocess: preprocess(), + + kit: { + adapter: adapter(), + + // Override http methods in the Todo forms + methodOverride: { + allowed: ['PATCH', 'DELETE'] + } + } +}; + +export default config; diff --git a/packages/create-svelte/shared/-typescript/jsconfig.json b/packages/create-svelte/shared/-typescript/jsconfig.json deleted file mode 100644 index 81ff9770cd8a..000000000000 --- a/packages/create-svelte/shared/-typescript/jsconfig.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json" -} diff --git a/packages/create-svelte/templates/default/src/hooks.ts b/packages/create-svelte/templates/default/src/hooks.ts index d767555dd9ea..484afff5d85b 100644 --- a/packages/create-svelte/templates/default/src/hooks.ts +++ b/packages/create-svelte/templates/default/src/hooks.ts @@ -2,6 +2,7 @@ import cookie from 'cookie'; import { v4 as uuid } from '@lukeed/uuid'; import type { Handle } from '@sveltejs/kit'; +/** @type {import('@sveltejs/kit').Handle} */ export const handle: Handle = async ({ event, resolve }) => { const cookies = cookie.parse(event.request.headers.get('cookie') || ''); event.locals.userid = cookies.userid || uuid(); diff --git a/packages/create-svelte/templates/default/src/lib/Counter.svelte b/packages/create-svelte/templates/default/src/lib/Counter.svelte index bb7127140914..50930bb2b742 100644 --- a/packages/create-svelte/templates/default/src/lib/Counter.svelte +++ b/packages/create-svelte/templates/default/src/lib/Counter.svelte @@ -7,6 +7,10 @@ $: displayed_count.set(count); $: offset = modulo($displayed_count, 1); + /** + * @param {number} n + * @param {number} m + */ function modulo(n: number, m: number) { // handle negative numbers return ((n % m) + m) % m; diff --git a/packages/create-svelte/templates/default/src/lib/form.ts b/packages/create-svelte/templates/default/src/lib/form.ts index fd52784942d3..c3a1933da66e 100644 --- a/packages/create-svelte/templates/default/src/lib/form.ts +++ b/packages/create-svelte/templates/default/src/lib/form.ts @@ -2,6 +2,32 @@ import { invalidate } from '$app/navigation'; // this action (https://svelte.dev/tutorial/actions) allows us to // progressively enhance a
that already works without JS +/** + * @param {HTMLFormElement} form + * @param {{ + * pending?: ({ data, form }: { data: FormData; form: HTMLFormElement }) => void; + * error?: ({ + * data, + * form, + * response, + * error + * }: { + * data: FormData; + * form: HTMLFormElement; + * response: Response | null; + * error: Error | null; + * }) => void; + * result?: ({ + * data, + * form, + * response + * }: { + * data: FormData; + * response: Response; + * form: HTMLFormElement; + * }) => void; + * }} [opts] + */ export function enhance( form: HTMLFormElement, { @@ -31,10 +57,11 @@ export function enhance( form: HTMLFormElement; }) => void; } = {} -): { destroy: () => void } { +) { let current_token: unknown; - async function handle_submit(e: Event) { + /** @param {SubmitEvent} e */ + async function handle_submit(e: SubmitEvent) { const token = (current_token = {}); e.preventDefault(); diff --git a/packages/create-svelte/templates/default/src/routes/todos/_api.ts b/packages/create-svelte/templates/default/src/routes/todos/_api.ts index 397cdda79151..d627ea021b4b 100644 --- a/packages/create-svelte/templates/default/src/routes/todos/_api.ts +++ b/packages/create-svelte/templates/default/src/routes/todos/_api.ts @@ -11,6 +11,11 @@ const base = 'https://api.svelte.dev'; +/** + * @param {string} method + * @param {string} resource + * @param {Record} [data] + */ export function api(method: string, resource: string, data?: Record) { return fetch(`${base}/${resource}`, { method, diff --git a/packages/create-svelte/templates/default/src/routes/todos/index.svelte b/packages/create-svelte/templates/default/src/routes/todos/index.svelte index 7f997c23d033..cd84d0b89695 100644 --- a/packages/create-svelte/templates/default/src/routes/todos/index.svelte +++ b/packages/create-svelte/templates/default/src/routes/todos/index.svelte @@ -3,6 +3,16 @@ import { scale } from 'svelte/transition'; import { flip } from 'svelte/animate'; + /** + * @typedef {{ + * uid: string; + * created_at: Date; + * text: string; + * done: boolean; + * pending_delete: boolean; + * }} Todo + */ + type Todo = { uid: string; created_at: Date; @@ -11,6 +21,7 @@ pending_delete: boolean; }; + /** @type {Todo[]} */ export let todos: Todo[]; diff --git a/packages/create-svelte/templates/default/src/routes/todos/index.ts b/packages/create-svelte/templates/default/src/routes/todos/index.ts index fc8bd4b8efaf..5ca318899587 100644 --- a/packages/create-svelte/templates/default/src/routes/todos/index.ts +++ b/packages/create-svelte/templates/default/src/routes/todos/index.ts @@ -1,6 +1,7 @@ import { api } from './_api'; -import type { RequestHandler } from '@sveltejs/kit'; +import type { RequestHandler } from './index'; +/** @type {import('./index').RequestHandler} */ export const get: RequestHandler = async ({ locals }) => { // locals.userid comes from src/hooks.js const response = await api('get', `todos/${locals.userid}`); @@ -28,6 +29,7 @@ export const get: RequestHandler = async ({ locals }) => { }; }; +/** @type {import('./index').RequestHandler} */ export const post: RequestHandler = async ({ request, locals }) => { const form = await request.formData(); @@ -47,6 +49,7 @@ const redirect = { } }; +/** @type {import('./index').RequestHandler} */ export const patch: RequestHandler = async ({ request, locals }) => { const form = await request.formData(); @@ -58,6 +61,7 @@ export const patch: RequestHandler = async ({ request, locals }) => { return redirect; }; +/** @type {import('./index').RequestHandler} */ export const del: RequestHandler = async ({ request, locals }) => { const form = await request.formData(); diff --git a/packages/create-svelte/types/internal.d.ts b/packages/create-svelte/types/internal.d.ts index 115e9296ea6c..e0a6ede97b72 100644 --- a/packages/create-svelte/types/internal.d.ts +++ b/packages/create-svelte/types/internal.d.ts @@ -1,7 +1,7 @@ export type Options = { name: string; template: 'default' | 'skeleton'; - typescript: boolean; + types: 'typescript' | 'checkjs' | null; prettier: boolean; eslint: boolean; playwright: boolean; @@ -16,6 +16,7 @@ export type Condition = | 'eslint' | 'prettier' | 'typescript' + | 'checkjs' | 'playwright' | 'skeleton' | 'default';