Project & Tooling
scaffold ด้วย sv CLI
หัวข้อที่มีชื่อว่า “scaffold ด้วย sv CLI”วิธีเริ่มโปรเจกต์ Svelte หรือ SvelteKit ในปัจจุบันคือ sv CLI คำสั่งเดียว scaffold โปรเจกต์และเพิ่ม integration ได้ (TypeScript, Prettier, ESLint, Tailwind, testing) แบบ interactive:
npx sv create my-appcd my-appnpm installnpm run devsv create มาแทน flow เดิม npm create svelte@latest โดยตั้งค่าโปรเจกต์ SvelteKit ให้เป็น default (วิธีที่แนะนำในการสร้างแอป Svelte), wire up Vite ให้ และให้คุณเลือก add-on ตั้งแต่ต้น มี npx sv add <name> สำหรับเพิ่ม integration เข้าโปรเจกต์ที่มีอยู่แล้วภายหลังด้วย
Vite ขับเคลื่อนทุกอย่าง
หัวข้อที่มีชื่อว่า “Vite ขับเคลื่อนทุกอย่าง”โปรเจกต์ Svelte รันบน Vite ตอน development Vite serve โมดูลของคุณผ่าน native ESM พร้อม hot module replacement — แก้ component แล้วเห็นการเปลี่ยนแปลงโดยไม่ต้อง reload ทั้งหน้า และรักษา state ไว้ได้เท่าที่ทำได้ ส่วน production vite build จะ bundle และ optimize output
{ "scripts": { "dev": "vite dev", "build": "vite build", "preview": "vite preview", "check": "svelte-check --tsconfig ./tsconfig.json" }}Svelte integration ของ Vite (@sveltejs/vite-plugin-svelte ที่ sv create wire ให้แล้ว) คือสิ่งที่รัน compiler บนไฟล์ .svelte ตอน dev และ build คุณแทบไม่ต้องแตะ plugin ตัวนี้ตรง ๆ
flowchart LR code["your .svelte / .ts files"] --> vite["Vite + svelte plugin"] vite --> dev["dev: HMR server"] vite --> build["build: compiled, bundled output"]
editor support และ svelte-check
หัวข้อที่มีชื่อว่า “editor support และ svelte-check”มีเครื่องมือสองตัวที่ช่วยให้ประสบการณ์การเขียนตรงไปตรงมา:
- Svelte editor extension (ทางการ สำหรับ VS Code และตัวอื่น) ให้ syntax highlighting, autocompletion และ inline diagnostics สำหรับไฟล์
.svelte— รวมถึง type error ใน markup ไม่ใช่แค่ใน script svelte-checkคือเวอร์ชัน command-line: type-check และ lint ทั้งโปรเจกต์ รวมถึง expression ใน template คุณจึงจับ error ใน CI แบบเดียวกับที่ editor โชว์ให้ตอนอยู่ในเครื่อง
npm run check # runs svelte-check across the projectเพราะ svelte-check เข้าใจไฟล์ .svelte แบบครบวงจร จึงจับ error ที่ tsc ธรรมดามองไม่เห็น — prop type ที่ส่งผิดใน markup, typo ใน template expression, CSS selector ที่ไม่ได้ใช้
svelte-check พิสูจน์ด้วย error จริง
หัวข้อที่มีชื่อว่า “svelte-check พิสูจน์ด้วย error จริง”“จับ prop type ที่ส่งผิดใน markup” ไม่ใช่แค่ทฤษฎี — นี่คือ component ที่มี prop ระบุ type และ caller ที่ส่ง type ผิด:
<script lang="ts"> interface Props { name: string; } let { name }: Props = $props();</script><p>Hello {name.toUpperCase()}</p><!-- @expect-error Type 'number' is not assignable to type 'string'. --><script lang="ts"> import Greeter from './Greeter.svelte';</script><Greeter name={42} />รัน svelte-check กับโปรเจกต์จริง (output จริง ตัด path ออก):
$ svelte-check --tsconfig ./tsconfig.jsonsrc/lib/BadUsage.svelte:4:10Type 'number' is not assignable to type 'string'.รัน tsc ธรรมดากับโปรเจกต์นี้จะไม่จับ error นี้เลย — type error อยู่ข้างใน markup ของไฟล์ .svelte (<Greeter name={42} />) ซึ่งมีแค่เครื่องมือที่เข้าใจ format .svelte จริง ๆ ไม่ใช่แค่ .ts/.tsx เท่านั้นที่ type-check ได้
Lint ไฟล์ .svelte: eslint-plugin-svelte
หัวข้อที่มีชื่อว่า “Lint ไฟล์ .svelte: eslint-plugin-svelte”Type-checking เป็นแกนหนึ่ง linting เป็นอีกแกน sv create wire ESLint ให้พร้อม eslint-plugin-svelte ซึ่งเข้าใจ pattern เฉพาะของ Svelte ที่ linter JS/TS ทั่วไปไม่มี concept เลย — {@html} ที่ไม่ escape, {#each} ที่ไม่มี key, directive on: ที่เลิกใช้แล้ว มันรองรับแค่ flat config (eslint.config.js) ของ ESLint สมัยใหม่เท่านั้น ตรงกับ ESLint ปัจจุบัน:
import svelteConfig from './svelte.config.js';import { defineConfig } from 'eslint/config';import globals from 'globals';import js from '@eslint/js';import svelte from 'eslint-plugin-svelte';
export default defineConfig([ js.configs.recommended, svelte.configs.recommended, { languageOptions: { globals: { ...globals.browser, ...globals.node }, }, }, { files: ['**/*.svelte', '**/*.svelte.js', '**/*.svelte.ts'], languageOptions: { parserOptions: { svelteConfig } }, },]);รันกับ component ที่ inject HTML ที่ไม่ sanitize:
<script> let unsafeHtml = $state('<b>hi</b>'); let count = $state(0);</script>
{@html unsafeHtml}<button onclick={() => count++}>{count}</button>output จริง (eslint-plugin-svelte 3.23.0, ESLint 10):
$ npx eslint src/lib/LintViolation.svelte/.../src/lib/LintViolation.svelte 6:1 error `{@html}` can lead to XSS attack svelte/no-at-html-tags
✖ 1 problem (1 error, 0 warnings)svelte.configs.recommended รวม rule แบบนี้ไว้ — บวก rule ที่รู้จัก deprecation ที่ flag on:click และ pattern ยุค Svelte 4 อื่น ๆ — ดังนั้นขั้น lint ใน CI จับพวกนี้ได้ก่อน PR merge ไม่ใช่แค่ตอนที่ svelte-check บังเอิญสังเกตเห็น
vite-plugin-svelte: compiler option ที่อยู่ข้างใต้ทุกอย่าง
หัวข้อที่มีชื่อว่า “vite-plugin-svelte: compiler option ที่อยู่ข้างใต้ทุกอย่าง”@sveltejs/vite-plugin-svelte (ที่ sv create wire ให้ พูดถึงไปแล้วก่อนหน้าในบทนี้) คือจุดที่ compiler option ทุกตัวถูกตั้งค่าสุดท้ายจริง ๆ สำหรับทั้งโปรเจกต์ ใน svelte.config.js:
export default { compilerOptions: { // e.g. { runes: true } to force runes mode project-wide (see the // "Why Svelte, the Compiler" lesson for what this overrides) }, vitePlugin: { // dynamicCompileOptions lets you vary compiler options PER FILE — // e.g. forcing runes mode only for files under one directory during // an incremental migration. experimental: { dynamicCompileOptions({ filename, compileOptions }) { if (filename.includes('/src/routes/legacy/') || compileOptions.runes) return; return { runes: true }; }, }, },};flag experimental.async ที่บท Async Svelteของคอร์สนี้พิสูจน์ไว้ — ต้องเปิดก่อน await จะใช้ได้ข้างใน $derived หรือ template expression — ถูกตั้งแบบเดียวกันเป๊ะ เป็น entry ของ compilerOptions.experimental; compile error จริงของบทนั้น (Cannot use \await` in deriveds and template expressions… unless the `experimental.async` compiler option is `true“) คือสิ่งที่เกิดขึ้นเมื่อ switch ระดับโปรเจกต์นี้ยังไม่เปิด
Migrate โค้ด Svelte 4: sv migrate svelte-5
หัวข้อที่มีชื่อว่า “Migrate โค้ด Svelte 4: sv migrate svelte-5”สำหรับโปรเจกต์ Svelte 4 ที่มีอยู่แล้ว sv CLI ก็ขับ migration เองด้วย:
npx sv migrate svelte-5ตามmigration guide: มัน “automates repetitive tasks such as updating dependencies, converting let declarations to runes, migrating event attributes like on:click to onclick, and transforming slot-based patterns into the new snippet and render tag syntax” มันจัดการการ rewrite เชิงกลไก; ตารางการจำโค้ด legacy ในบท events & attributes คือที่อ้างอิงว่าแต่ละการแปลงนั้นเล็งไปที่อะไร และสำหรับ compile error จริงที่คุณจะเจอกับสิ่งที่ script อัตโนมัติทำไม่ครบ
Mental Model
หัวข้อที่มีชื่อว่า “Mental Model”ทุกอย่างในบทนี้คือ tooling ที่สร้างขึ้น รอบ ข้อเท็จจริงเดียว: ไฟล์ .svelte ไม่ใช่ JavaScript ธรรมดา ดังนั้นเครื่องมือใดก็ตามที่เข้าใจแค่ JS/TS (tsc, config ESLint ทั่วไป) จะมองไม่เห็นความผิดพลาดระดับ markup โดยโครงสร้าง svelte-check และ eslint-plugin-svelte ต่างมีอยู่เพราะต้องมีใครสักคน parse format .svelte เองก่อนที่ type-checking หรือ linting จะมองเห็นข้างใน markup ได้ — นั่นคือเหตุผลทั้งหมดที่มันเป็นเครื่องมือแยกต่างหาก แทนที่ tsc/eslint จะทำงานได้เองตั้งแต่แรก
ข้อแลกเปลี่ยน
หัวข้อที่มีชื่อว่า “ข้อแลกเปลี่ยน”| ได้อะไร | ประโยชน์ | ต้นทุน |
|---|---|---|
svelte-check แทน tsc ธรรมดา | จับ error ข้างใน markup ได้ — พิสูจน์แล้วด้านบนด้วย prop type ที่ผิด ที่ tsc เพียวมองไม่เห็น | เป็นคำสั่งที่สองใน CI ที่ช้ากว่า; ต้องเรียก --tsconfig ของตัวเองแยก |
eslint-plugin-svelte flat config | rule ที่รู้จัก Svelte (ความเสี่ยง {@html}, on: ที่เลิกใช้, {#each} ไม่มี key) — พิสูจน์แล้วด้านบน | รองรับแค่ flat config; โปรเจกต์ที่ยังใช้ .eslintrc.js ต้อง migrate ก่อน |
dynamicCompileOptions สำหรับ compiler setting ต่อไฟล์ | codebase ใหญ่บังคับ runes mode ทีละไฟล์ตอน migrate ได้ แทนที่จะเป็น all-or-nothing | config ของโปรเจกต์เพิ่มอีกชั้นที่ต้องคิดตาม นอกเหนือจาก object compilerOptions ธรรมดา |
sv migrate svelte-5 | ทำส่วนที่เป็นกลไกของการ upgrade Svelte 4 → 5 ให้อัตโนมัติ | ไม่ได้ครอบคลุมทุกเคส — อะไรที่มันพลาดจะโผล่เป็น compile error จริงตามตารางการจำโค้ด legacy |
ความเข้าใจผิดที่พบบ่อย
หัวข้อที่มีชื่อว่า “ความเข้าใจผิดที่พบบ่อย”- “
tscเพียวก็พอสำหรับ type-check โปรเจกต์ Svelte” พิสูจน์แล้วด้านบน: prop type ที่ส่งผิดใน markup ของไฟล์.svelteมองไม่เห็นสำหรับtscธรรมดาเลย — มีแค่svelte-checkที่ parse format Svelte จริง ๆ เท่านั้นที่จับได้ - “config ESLint ไหนก็ใช้กับไฟล์
.svelteได้”eslint-plugin-sveltev3 ต้องการ flat config (eslint.config.js) เท่านั้น และต้องมีsvelte-eslint-parserถึงจะอ่านไฟล์ได้เลยด้วยซ้ำ — setup.eslintrc.jsแบบเก่าไม่ lint markup ของไฟล์.svelteเลย - “
experimental.asyncเป็น setting ของ<svelte:options>หรือต่อ component” มันคือ compiler option ระดับโปรเจกต์ (หรือต่อไฟล์ผ่านdynamicCompileOptions) ที่ตั้งผ่านvite-plugin-svelte/svelte.config.js— ไม่ใช่อะไรที่คุณ toggle ต่อ component
อ่านโค้ดที่ AI เขียน
หัวข้อที่มีชื่อว่า “อ่านโค้ดที่ AI เขียน”เวลารีวิว project configuration และ tooling setup ให้เช็ค:
- CI รันแค่
tsc --noEmit(หรือไม่รันอะไรเลย) สำหรับโปรเจกต์ SvelteKit โดยไม่มีขั้นsvelte-checkเลยหรือเปล่า — แปลว่า type error ระดับ markup แบบที่พิสูจน์แล้วด้านบน ship ออกไปแบบเงียบ ๆ? - มี
.eslintrc.js/.eslintrc.jsonอยู่คู่กับeslint-plugin-svelteหรือเปล่า — คู่นี้ใช้ด้วยกันไม่ได้ เพราะ major version ปัจจุบันของ plugin ต้องการ flat config เท่านั้น? - บทเรียน, README หรือ snippet ที่ generate มายังพูดถึง
npm create svelte@latestหรืออ้างsvelte-migrateเป็น package แยกต่างหาก แทนที่จะเป็นnpx sv create/npx sv migrate svelte-5ปัจจุบันหรือเปล่า? - compiler option อย่าง
experimental.asyncถูกตั้งไว้ต่อ component ที่ไหนสักแห่ง (เป็น prop, comment) แทนที่จะอยู่ในcompilerOptions/vitePluginของsvelte.config.jsซึ่งเป็นที่ที่มันต้องอยู่จริง ๆ หรือเปล่า?
🐛 หา bug ในโค้ดนี้
import js from '@eslint/js';
export default [js.configs.recommended,// ... rest of the config, no Svelte-specific plugin at all];เฉลย
config นี้ lint ไฟล์ .js/.ts ธรรมดาได้ปกติ แต่ไม่พูดถึง eslint-plugin-svelte หรือ parser ของมันเลย — ดังนั้นทุกไฟล์ .svelte ในโปรเจกต์จะถูกข้ามไปเลย หรือถ้าถูกรวมผ่าน glob ก็จะถูก parse เหมือนเป็น JavaScript ธรรมดา (ซึ่งมันไม่ใช่: markup, {expressions} และ directive ไม่ใช่ JS syntax ที่ถูกต้อง) ไม่ว่าทางไหน rule เฉพาะของ Svelte ที่พิสูจน์แล้วด้านบน (อย่าง error จริงของ svelte/no-at-html-tags) ไม่เคย run เลย และ review ที่ดูแค่ว่า “ESLint ผ่านไหม” จะได้ความมั่นใจปลอม ๆ
วิธีแก้คือ flat config จากบทนี้: import eslint-plugin-svelte, extend svelte.configs.recommended และชี้ไฟล์ .svelte/.svelte.js/.svelte.ts ไปที่ parser ของ Svelte ผ่าน languageOptions.parserOptions.svelteConfig