Repository: sveltejs/language-tools
Pull request #1154 (2021-08-30 16:27:38)

(fix) preserve quotes in event handler
#1153


Patch:
diff --git a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts
index 9383c75b..be2b937f 100644
--- a/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts
+++ b/packages/svelte2tsx/src/htmlxtojsx/utils/node-utils.ts
@@ -120,7 +120,10 @@ function getNameValuePairsFromAttributes(
                     return { name, value: name, identifier: name };
                 }
                 if (val.type === 'Text') {
-                    return { name, value: `'${val.data || val.raw}'` };
+                    const quote = ['"', "'"].includes(originalStr[val.start - 1])
+                        ? originalStr[val.start - 1]
+                        : "'";
+                    return { name, value: `${quote}${val.data || val.raw}${quote}` };
                 }
                 if (val.type === 'MustacheTag') {
                     const valueStr = originalStr.substring(val.start + 1, val.end - 1);


Test patch:
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/component-slot-infer-props/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/component-slot-infer-props/expected.jsx
index dfff877e..7c3fe115 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/component-slot-infer-props/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/component-slot-infer-props/expected.jsx
@@ -1,5 +1,5 @@
-<><Parent bare shorthand={shorthand} text1="val1" text2="val2" text3={`a${a}b${b}`} textEmpty="" literal={true} strLiteral={'foo'} complex={{a}} a-dashed-complex={{a}} {...__sveltets_1_cssProp({"--custom-cssprop": `foo`})} >{() => {/*Ωignore_startΩ*/const Ψcomplex={a},Ψa_dashed_complex={a};/*Ωignore_endΩ*/() => { let {foo} = /*Ωignore_startΩ*/new Parent({target: __sveltets_1_any(''), props: {'bare':true, 'shorthand':shorthand, 'text1':'val1', 'text2':'val2', 'text3':`a${a}b${b}`, 'textEmpty':"", 'literal':true, 'strLiteral':'foo', 'complex':Ψcomplex, 'a-dashed-complex':Ψa_dashed_complex}})/*Ωignore_endΩ*/.$$slot_def['default'];<>
-    {() => { let {bar} = /*Ωignore_startΩ*/new Parent({target: __sveltets_1_any(''), props: {'bare':true, 'shorthand':shorthand, 'text1':'val1', 'text2':'val2', 'text3':`a${a}b${b}`, 'textEmpty':"", 'literal':true, 'strLiteral':'foo', 'complex':Ψcomplex, 'a-dashed-complex':Ψa_dashed_complex}})/*Ωignore_endΩ*/.$$slot_def['named'];<><Component  >
+<><Parent bare shorthand={shorthand} text1="val1" text2="val2" text3={`a${a}b${b}`} textEmpty="" literal={true} strLiteral={'foo'} complex={{a}} a-dashed-complex={{a}} {...__sveltets_1_cssProp({"--custom-cssprop": `foo`})} >{() => {/*Ωignore_startΩ*/const Ψcomplex={a},Ψa_dashed_complex={a};/*Ωignore_endΩ*/() => { let {foo} = /*Ωignore_startΩ*/new Parent({target: __sveltets_1_any(''), props: {'bare':true, 'shorthand':shorthand, 'text1':'val1', 'text2':"val2", 'text3':`a${a}b${b}`, 'textEmpty':"", 'literal':true, 'strLiteral':'foo', 'complex':Ψcomplex, 'a-dashed-complex':Ψa_dashed_complex}})/*Ωignore_endΩ*/.$$slot_def['default'];<>
+    {() => { let {bar} = /*Ωignore_startΩ*/new Parent({target: __sveltets_1_any(''), props: {'bare':true, 'shorthand':shorthand, 'text1':'val1', 'text2':"val2", 'text3':`a${a}b${b}`, 'textEmpty':"", 'literal':true, 'strLiteral':'foo', 'complex':Ψcomplex, 'a-dashed-complex':Ψa_dashed_complex}})/*Ωignore_endΩ*/.$$slot_def['named'];<><Component  >
         {foo} {bar}
     </Component></>}}
     <div>
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/event-handler-component-infer-props/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/event-handler-component-infer-props/expected.jsx
index 349713e7..ab7b5bb4 100644
--- a/packages/svelte2tsx/test/htmlx2jsx/samples/event-handler-component-infer-props/expected.jsx
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/event-handler-component-infer-props/expected.jsx
@@ -1,1 +1,1 @@
-<><Component bare shorthand={shorthand} text1="val1" text2="val2" text3={`a${a}b${b}`} textEmpty="" literal={true} strLiteral={'foo'} complex={{a}} a-dashed-complex={{a}} {...__sveltets_1_cssProp({"--custom-cssprop": `foo`})}  />{/*Ωignore_startΩ*/new Component({target: __sveltets_1_any(''), props: {'bare':true, 'shorthand':shorthand, 'text1':'val1', 'text2':'val2', 'text3':`a${a}b${b}`, 'textEmpty':"", 'literal':true, 'strLiteral':'foo', 'complex':{a}, 'a-dashed-complex':{a}}})/*Ωignore_endΩ*/.$on('click', e => e)}</>
\ No newline at end of file
+<><Component bare shorthand={shorthand} text1="val1" text2="val2" text3={`a${a}b${b}`} textEmpty="" literal={true} strLiteral={'foo'} complex={{a}} a-dashed-complex={{a}} {...__sveltets_1_cssProp({"--custom-cssprop": `foo`})}  />{/*Ωignore_startΩ*/new Component({target: __sveltets_1_any(''), props: {'bare':true, 'shorthand':shorthand, 'text1':'val1', 'text2':"val2", 'text3':`a${a}b${b}`, 'textEmpty':"", 'literal':true, 'strLiteral':'foo', 'complex':{a}, 'a-dashed-complex':{a}}})/*Ωignore_endΩ*/.$on('click', e => e)}</>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/quotes-inside-quotes/expected.jsx b/packages/svelte2tsx/test/htmlx2jsx/samples/quotes-inside-quotes/expected.jsx
new file mode 100644
index 00000000..e5cb18df
--- /dev/null
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/quotes-inside-quotes/expected.jsx
@@ -0,0 +1,2 @@
+<><Component placeholder="'"  />{/*Ωignore_startΩ*/new Component({target: __sveltets_1_any(''), props: {'placeholder':"'"}})/*Ωignore_endΩ*/.$on('keydown', () => {})}
+<Component placeholder='"'  />{/*Ωignore_startΩ*/new Component({target: __sveltets_1_any(''), props: {'placeholder':'"'}})/*Ωignore_endΩ*/.$on('keydown', () => {})}</>
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/htmlx2jsx/samples/quotes-inside-quotes/input.svelte b/packages/svelte2tsx/test/htmlx2jsx/samples/quotes-inside-quotes/input.svelte
new file mode 100644
index 00000000..31a868ff
--- /dev/null
+++ b/packages/svelte2tsx/test/htmlx2jsx/samples/quotes-inside-quotes/input.svelte
@@ -0,0 +1,2 @@
+<Component placeholder="'" on:keydown={() => {}} />
+<Component placeholder='"' on:keydown={() => {}} />
\ No newline at end of file
diff --git a/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx b/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx
index e98ebbef..3584156d 100644
--- a/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx
+++ b/packages/svelte2tsx/test/sourcemaps/samples/large-sample-1/mappings.jsx
@@ -429,15 +429,15 @@ s
                 injectedJS={mapbox_setup}
                 relaxed                                                                                                                               {/**
 ------------------------------------------------------------------------------------------------------------------------------------------------------ */}
-            />{/*Ωignore_startΩ*/new Repl({target: __sveltets_1_any(''), props: {'workersUrl':'workers', 'svelteUrl':svelteUrl, 'rollupUrl':rollupUrl, 'orientation':mobile ? 'columns' : 'rows', 'fixed':mobile, 'injectedJS':mapbox_setup, 'relaxed':true}})/*Ωignore_endΩ*/.$on('change', handle_change)}{/**
-         ╚╚╚/>{/*Ωignore_startΩ*/new•Repl({target:•__sveltets_1_any(''),•props:•{'workersUrl':'workers',•'svelteUrl':svelteUrl,•'rollupUrl':rollupUrl,•'orientation':mobile•?•'columns'•:•'rows',•'fixed':mobile,•'injectedJS':mapbox_setup,•'relaxed':true}})/*Ωignore_endΩ*/.$on('change',•handle_change)}↲    [generated] line 172
+            />{/*Ωignore_startΩ*/new Repl({target: __sveltets_1_any(''), props: {'workersUrl':"workers", 'svelteUrl':svelteUrl, 'rollupUrl':rollupUrl, 'orientation':mobile ? 'columns' : 'rows', 'fixed':mobile, 'injectedJS':mapbox_setup, 'relaxed':true}})/*Ωignore_endΩ*/.$on('change', handle_change)}{/**
+         ╚╚╚/>{/*Ωignore_startΩ*/new•Repl({target:•__sveltets_1_any(''),•props:•{'workersUrl':"workers",•'svelteUrl':svelteUrl,•'rollupUrl':rollupUrl,•'orientation':mobile•?•'columns'•:•'rows',•'fixed':mobile,•'injectedJS':mapbox_setup,•'relaxed':true}})/*Ωignore_endΩ*/.$on('change',•handle_change)}↲    [generated] line 172
                                                                                                                                                                                                                                                                                 on('change',•handle_change)}     [generated] subset
                                                                                                                                                                                                                                                                                 on: change=  handle_change}      
              on:change= handle_change}                                                                                                                                                                                                                                                                           
          ╚╚╚╚on:change={handle_change}↲                                                                                                                                                                                                                                                                          [original] line 309 (rest generated at line 169)
                                                                                                                                                                                                                                                                                                                  
-         ╚╚╚/>{/*Ωignore_startΩ*/new•Repl({target:•__sveltets_1_any(''),•props:•{'workersUrl':'workers',•'svelteUrl':svelteUrl,•'rollupUrl':rollupUrl,•'orientation':mobile•?•'columns'•:•'rows',•'fixed':mobile,•'injectedJS':mapbox_setup,•'relaxed':true}})/*Ωignore_endΩ*/.$on('change',•handle_change)}↲    [generated] line 172
-         ╚╚╚/>{/*Ωignore_startΩ*/new•Repl({target:•__sveltets_1_any(''),•props:•{'workersUrl':'workers',•'svelteUrl':svelteUrl,•'rollupUrl':rollupUrl,•'orientation':mobile•?•'columns'•:•'rows',•'fixed':mobile,•'injectedJS':mapbox_setup,•'relaxed':true}})/*Ωignore_endΩ*/.$                            ↲    [generated] subset
+         ╚╚╚/>{/*Ωignore_startΩ*/new•Repl({target:•__sveltets_1_any(''),•props:•{'workersUrl':"workers",•'svelteUrl':svelteUrl,•'rollupUrl':rollupUrl,•'orientation':mobile•?•'columns'•:•'rows',•'fixed':mobile,•'injectedJS':mapbox_setup,•'relaxed':true}})/*Ωignore_endΩ*/.$on('change',•handle_change)}↲    [generated] line 172
+         ╚╚╚/>{/*Ωignore_startΩ*/new•Repl({target:•__sveltets_1_any(''),•props:•{'workersUrl':"workers",•'svelteUrl':svelteUrl,•'rollupUrl':rollupUrl,•'orientation':mobile•?•'columns'•:•'rows',•'fixed':mobile,•'injectedJS':mapbox_setup,•'relaxed':true}})/*Ωignore_endΩ*/.$                            ↲    [generated] subset
          ╚╚╚/>                                                                                                                                                                                                                                                                                              ↲    
          ╚╚╚/>↲                                                                                                                                                                                                                                                                                                  [original] line 312 
 ------------------------------------------------------------------------------------------------------------------------------------------------------ */}
diff --git a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-forward-with-props/expected.tsx b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-forward-with-props/expected.tsx
index c75da30f..873a413e 100644
--- a/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-forward-with-props/expected.tsx
+++ b/packages/svelte2tsx/test/svelte2tsx/samples/component-slot-forward-with-props/expected.tsx
@@ -1,7 +1,7 @@
 ///<reference types="svelte" />
 <></>;function render() {
 /*Ωignore_startΩ*/;const __sveltets_ensureSlot = __sveltets_1_createEnsureSlot();/*Ωignore_endΩ*/
-<><Parent propA propB={propB} propC="val1" propD="val2" propE={`a${a}b${b}`} >{() => { let {foo} = /*Ωignore_startΩ*/new Parent({target: __sveltets_1_any(''), props: {'propA':true, 'propB':propB, 'propC':'val1', 'propD':'val2', 'propE':`a${a}b${b}`}})/*Ωignore_endΩ*/.$$slot_def['default'];<>
+<><Parent propA propB={propB} propC="val1" propD="val2" propE={`a${a}b${b}`} >{() => { let {foo} = /*Ωignore_startΩ*/new Parent({target: __sveltets_1_any(''), props: {'propA':true, 'propB':propB, 'propC':'val1', 'propD':"val2", 'propE':`a${a}b${b}`}})/*Ωignore_endΩ*/.$$slot_def['default'];<>
     <slot foo={__sveltets_ensureSlot("default","foo",foo)} />
 </>}}</Parent></>
 return { props: {}, slots: {'default': {foo:__sveltets_1_instanceOf(Parent).$$slot_def['default'].foo}}, getters: {}, events: {} }}