-
-
Notifications
You must be signed in to change notification settings - Fork 2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add self-closing-tags migration
Companion to sveltejs/svelte#11114. This adds an npx svelte-migrate self-closing-tags migration that replaces all the self-closing non-void elements in your .svelte files.
- Loading branch information
1 parent
f71f381
commit 212e084
Showing
16 changed files
with
271 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"svelte-migrate": minor | ||
--- | ||
|
||
feat: add self-closing-tags migration |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/kit/test/apps/basics/src/routes/data-sveltekit/noscroll/+page.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 changes: 1 addition & 1 deletion
2
packages/kit/test/apps/basics/src/routes/data-sveltekit/noscroll/target/+page.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
<div style="height: 2000px; background: palegoldenrod" /> | ||
<div style="height: 2000px; background: palegoldenrod"></div> | ||
|
||
<h1>target</h1> |
2 changes: 1 addition & 1 deletion
2
packages/kit/test/apps/basics/src/routes/iframes/nested/parent/+page.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
<iframe title="Child content" src="./child" /> | ||
<iframe title="Child content" src="./child"></iframe> |
2 changes: 1 addition & 1 deletion
2
packages/kit/test/apps/basics/src/routes/no-ssr/margin/+page.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,4 +12,4 @@ | |
|
||
<a href="/routing/b" data-sveltekit-reload>b</a> | ||
|
||
<div class="hydrate-test" /> | ||
<div class="hydrate-test"></div> |
2 changes: 1 addition & 1 deletion
2
packages/kit/test/apps/basics/src/routes/scroll/cross-document/a/+page.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
<h1>a</h1> | ||
|
||
<div style="height: 200vh; background: teal" /> | ||
<div style="height: 200vh; background: teal"></div> | ||
|
||
<a data-sveltekit-reload href="/scroll/cross-document/b">b</a> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import colors from 'kleur'; | ||
import fs from 'node:fs'; | ||
import prompts from 'prompts'; | ||
import glob from 'tiny-glob/sync.js'; | ||
import { remove_self_closing_tags } from './migrate.js'; | ||
|
||
export async function migrate() { | ||
console.log( | ||
colors.bold().yellow('\nThis will update .svelte files inside the current directory\n') | ||
); | ||
|
||
const response = await prompts({ | ||
type: 'confirm', | ||
name: 'value', | ||
message: 'Continue?', | ||
initial: false | ||
}); | ||
|
||
if (!response.value) { | ||
process.exit(1); | ||
} | ||
|
||
const files = glob('**/*.svelte') | ||
.map((file) => file.replace(/\\/g, '/')) | ||
.filter((file) => !file.includes('/node_modules/')); | ||
|
||
for (const file of files) { | ||
try { | ||
const code = await remove_self_closing_tags(fs.readFileSync(file, 'utf-8')); | ||
fs.writeFileSync(file, code); | ||
} catch (e) { | ||
// continue | ||
} | ||
} | ||
|
||
console.log(colors.bold().green('✔ Your project has been updated')); | ||
console.log(' If using Prettier, please upgrade to the latest prettier-plugin-svelte version'); | ||
} |
184 changes: 184 additions & 0 deletions
184
packages/migrate/migrations/self-closing-tags/migrate.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,184 @@ | ||
import MagicString from 'magic-string'; | ||
import { parse, preprocess, walk } from 'svelte/compiler'; | ||
|
||
const VoidElements = [ | ||
'area', | ||
'base', | ||
'br', | ||
'col', | ||
'embed', | ||
'hr', | ||
'img', | ||
'input', | ||
'keygen', | ||
'link', | ||
'menuitem', | ||
'meta', | ||
'param', | ||
'source', | ||
'track', | ||
'wbr' | ||
]; | ||
|
||
const SVGElements = [ | ||
'altGlyph', | ||
'altGlyphDef', | ||
'altGlyphItem', | ||
'animate', | ||
'animateColor', | ||
'animateMotion', | ||
'animateTransform', | ||
'circle', | ||
'clipPath', | ||
'color-profile', | ||
'cursor', | ||
'defs', | ||
'desc', | ||
'discard', | ||
'ellipse', | ||
'feBlend', | ||
'feColorMatrix', | ||
'feComponentTransfer', | ||
'feComposite', | ||
'feConvolveMatrix', | ||
'feDiffuseLighting', | ||
'feDisplacementMap', | ||
'feDistantLight', | ||
'feDropShadow', | ||
'feFlood', | ||
'feFuncA', | ||
'feFuncB', | ||
'feFuncG', | ||
'feFuncR', | ||
'feGaussianBlur', | ||
'feImage', | ||
'feMerge', | ||
'feMergeNode', | ||
'feMorphology', | ||
'feOffset', | ||
'fePointLight', | ||
'feSpecularLighting', | ||
'feSpotLight', | ||
'feTile', | ||
'feTurbulence', | ||
'filter', | ||
'font', | ||
'font-face', | ||
'font-face-format', | ||
'font-face-name', | ||
'font-face-src', | ||
'font-face-uri', | ||
'foreignObject', | ||
'g', | ||
'glyph', | ||
'glyphRef', | ||
'hatch', | ||
'hatchpath', | ||
'hkern', | ||
'image', | ||
'line', | ||
'linearGradient', | ||
'marker', | ||
'mask', | ||
'mesh', | ||
'meshgradient', | ||
'meshpatch', | ||
'meshrow', | ||
'metadata', | ||
'missing-glyph', | ||
'mpath', | ||
'path', | ||
'pattern', | ||
'polygon', | ||
'polyline', | ||
'radialGradient', | ||
'rect', | ||
'set', | ||
'solidcolor', | ||
'stop', | ||
'svg', | ||
'switch', | ||
'symbol', | ||
'text', | ||
'textPath', | ||
'tref', | ||
'tspan', | ||
'unknown', | ||
'use', | ||
'view', | ||
'vkern' | ||
]; | ||
|
||
/** @param {string} source */ | ||
export async function remove_self_closing_tags(source) { | ||
const preprocessed = await preprocess(source, { | ||
script: ({ content }) => ({ | ||
code: content | ||
.split('\n') | ||
.map((line) => ' '.repeat(line.length)) | ||
.join('\n') | ||
}), | ||
style: ({ content }) => ({ | ||
code: content | ||
.split('\n') | ||
.map((line) => ' '.repeat(line.length)) | ||
.join('\n') | ||
}) | ||
}); | ||
const ast = parse(preprocessed.code); | ||
const ms = new MagicString(source); | ||
/** @type {Array<() => void>} */ | ||
const updates = []; | ||
let is_foreign = false; | ||
let is_custom_element = false; | ||
|
||
walk(/** @type {any} */ (ast.html), { | ||
/** @param {Record<string, any>} node */ | ||
enter(node) { | ||
if (node.type === 'Options') { | ||
const namespace = node.attributes.find( | ||
/** @param {any} a */ | ||
(a) => a.type === 'Attribute' && a.name === 'namespace' | ||
); | ||
if (namespace?.value[0].data === 'foreign') { | ||
is_foreign = true; | ||
return; | ||
} | ||
|
||
is_custom_element = node.attributes.some( | ||
/** @param {any} a */ | ||
(a) => a.type === 'Attribute' && (a.name === 'customElement' || a.name === 'tag') | ||
); | ||
} | ||
|
||
if (node.type === 'Element' || node.type === 'Slot') { | ||
const is_self_closing = source[node.end - 2] === '/'; | ||
if ( | ||
!is_self_closing || | ||
VoidElements.includes(node.name) || | ||
SVGElements.includes(node.name) || | ||
!/^[a-zA-Z0-9_-]+$/.test(node.name) | ||
) { | ||
return; | ||
} | ||
|
||
let start = node.end - 2; | ||
if (source[start - 1] === ' ') { | ||
start--; | ||
} | ||
updates.push(() => { | ||
if (node.type === 'Element' || is_custom_element) { | ||
ms.update(start, node.end, `></${node.name}>`); | ||
} | ||
}); | ||
} | ||
} | ||
}); | ||
|
||
if (is_foreign) { | ||
return source; | ||
} | ||
|
||
updates.forEach((update) => update()); | ||
return ms.toString(); | ||
} |
30 changes: 30 additions & 0 deletions
30
packages/migrate/migrations/self-closing-tags/migrate.spec.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
import { assert, test } from 'vitest'; | ||
import { remove_self_closing_tags } from './migrate.js'; | ||
|
||
/** @type {Record<string, string>} */ | ||
const tests = { | ||
'<div/>': '<div></div>', | ||
'<div />': '<div></div>', | ||
'<custom-element />': '<custom-element></custom-element>', | ||
'<div class="foo"/>': '<div class="foo"></div>', | ||
'<div class="foo" />': '<div class="foo"></div>', | ||
'\t<div\n\t\tonclick={blah}\n\t/>': '\t<div\n\t\tonclick={blah}\n\t></div>', | ||
'<foo-bar/>': '<foo-bar></foo-bar>', | ||
'<link/>': '<link/>', | ||
'<link />': '<link />', | ||
'<svg><g /></svg>': '<svg><g /></svg>', | ||
'<slot />': '<slot />', | ||
'<svelte:options customElement="my-element" /><slot />': | ||
'<svelte:options customElement="my-element" /><slot></slot>', | ||
'<svelte:options namespace="foreign" /><foo />': '<svelte:options namespace="foreign" /><foo />', | ||
'<script>console.log("<div />")</script>': '<script>console.log("<div />")</script>', | ||
'<script lang="ts">let a: string = ""</script><div />': | ||
'<script lang="ts">let a: string = ""</script><div></div>' | ||
}; | ||
|
||
for (const input in tests) { | ||
test(input, async () => { | ||
const output = tests[input]; | ||
assert.equal(await remove_self_closing_tags(input), output); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters