Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) preserve bind: in new transformation #1596

Merged
merged 3 commits into from Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -4,7 +4,8 @@ import {
mapRangeToOriginal,
getLineAtPosition,
getNodeIfIsInStartTag,
isInHTMLTagRange
isInHTMLTagRange,
getNodeIfIsInHTMLStartTag
} from '../../../lib/documents';
import { filterAsync, isNotNullOrUndefined, pathToUrl, unique } from '../../../utils';
import { RenameProvider } from '../../interfaces';
Expand Down Expand Up @@ -503,12 +504,35 @@ export class RenameProviderImpl implements RenameProvider {
const { parent } = snapshot;

if (this.configManager.getConfig().svelte.useNewTransformation) {
let rangeStart = parent.offsetAt(location.range.start);
let prefixText = location.prefixText?.trimRight();

// rename needs to be prefixed in case of a bind shortand on a HTML element
if (!prefixText) {
const original = parent.getText({
start: Position.create(
location.range.start.line,
location.range.start.character - bind.length
),
end: location.range.end
});
if (
original.startsWith(bind) &&
getNodeIfIsInHTMLStartTag(parent.html, rangeStart)
) {
return {
...location,
prefixText: original.slice(bind.length) + '={',
suffixText: '}'
};
}
}

if (!prefixText || prefixText.slice(-1) !== ':') {
return location;
}

// prefix is of the form `oldVarName: ` -> hints at a shorthand
let rangeStart = parent.offsetAt(location.range.start);
// we need to make sure we only adjust shorthands on elements/components
if (
!getNodeIfIsInStartTag(parent.html, rangeStart) ||
Expand All @@ -524,12 +548,14 @@ export class RenameProviderImpl implements RenameProvider {
) {
return location;
}

prefixText = prefixText.slice(0, -1) + '={';
location = {
...location,
prefixText,
suffixText: '}'
};

// rename range needs to be adjusted in case of an attribute shortand
if (snapshot.getOriginalText().charAt(rangeStart - 1) === '{') {
rangeStart--;
Expand All @@ -539,6 +565,7 @@ export class RenameProviderImpl implements RenameProvider {
end: parent.positionAt(rangeEnd)
};
}

return location;
}

Expand Down
8 changes: 7 additions & 1 deletion packages/svelte2tsx/src/htmlxtojsx_v2/index.ts
Expand Up @@ -117,7 +117,13 @@ export function convertHtmlxToJsx(
handleComment(str, node);
break;
case 'Binding':
handleBinding(str, node as BaseDirective, parent, element);
handleBinding(
str,
node as BaseDirective,
parent,
element,
options.typingsNamespace === 'svelteHTML'
);
break;
case 'Class':
handleClassDirective(str, node as BaseDirective, element as Element);
Expand Down
47 changes: 32 additions & 15 deletions packages/svelte2tsx/src/htmlxtojsx_v2/nodes/Binding.ts
Expand Up @@ -4,16 +4,18 @@ import { BaseDirective, BaseNode } from '../../interfaces';
import { Element } from './Element';
import { InlineComponent } from './InlineComponent';

const oneWayBindingAttributes: Map<string, string> = new Map(
['clientWidth', 'clientHeight', 'offsetWidth', 'offsetHeight']
.map((e) => [e, 'HTMLDivElement'] as [string, string])
.concat(
['duration', 'buffered', 'seekable', 'seeking', 'played', 'ended'].map((e) => [
e,
'HTMLMediaElement'
])
)
);
const oneWayBindingAttributes: Set<string> = new Set([
'clientWidth',
'clientHeight',
'offsetWidth',
'offsetHeight',
'duration',
'buffered',
'seekable',
'seeking',
'played',
'ended'
]);
/**
* List of all binding names that are transformed to sth like `binding = variable`.
* This applies to readonly bindings and the this binding.
Expand All @@ -34,7 +36,8 @@ export function handleBinding(
str: MagicString,
attr: BaseDirective,
parent: BaseNode,
element: Element | InlineComponent
element: Element | InlineComponent,
preserveBind: boolean
): void {
// bind group on input
if (element instanceof Element && attr.name == 'group' && parent.name == 'input') {
Expand Down Expand Up @@ -70,11 +73,25 @@ export function handleBinding(

// other bindings which are transformed to normal attributes/props
const isShorthand = attr.expression.start === attr.start + 'bind:'.length;
const name: TransformationArray = isShorthand
? [[attr.expression.start, attr.expression.end]]
: [[attr.start + 'bind:'.length, str.original.lastIndexOf('=', attr.expression.start)]];
const name: TransformationArray =
preserveBind && element instanceof Element
? // HTML typings - preserve the bind: prefix
isShorthand
? [`"${str.original.substring(attr.start, attr.end)}"`]
: [
`"${str.original.substring(
attr.start,
str.original.lastIndexOf('=', attr.expression.start)
)}"`
]
: // Other typings - remove the bind: prefix
isShorthand
? [[attr.expression.start, attr.expression.end]]
: [[attr.start + 'bind:'.length, str.original.lastIndexOf('=', attr.expression.start)]];
const value: TransformationArray | undefined = isShorthand
? undefined
? preserveBind && element instanceof Element
? [rangeWithTrailingPropertyAccess(str.original, attr.expression)]
: undefined
: [rangeWithTrailingPropertyAccess(str.original, attr.expression)];
if (element instanceof Element) {
element.addAttribute(name, value);
Expand Down
@@ -1,2 +1,2 @@
{ svelteHTML.createElement("input", { "type":`text`,value,});}
{ svelteHTML.createElement("input", { "type":`checkbox`,checked,});}
{ svelteHTML.createElement("input", { "type":`text`,"bind:value":value,});}
{ svelteHTML.createElement("input", { "type":`checkbox`,"bind:checked":checked,});}
@@ -1,3 +1,3 @@
{ svelteHTML.createElement("input", { "type":`text`,value:test,});}
{ svelteHTML.createElement("input", { "type":`text`,value:test,});}
{ svelteHTML.createElement("input", { "type":`text`,value:test,});}
{ svelteHTML.createElement("input", { "type":`text`,"bind:value":test,});}
{ svelteHTML.createElement("input", { "type":`text`,"bind:value":test,});}
{ svelteHTML.createElement("input", { "type":`text`,"bind:value":test,});}
Expand Up @@ -4,4 +4,4 @@
{ svelteHTML.createElement("img", { });__sveltets_2_ensureTransition(fade(svelteHTML.mapElementTag('img'),(params)));}
{ svelteHTML.createElement("img", { });classthing;}
{ svelteHTML.createElement("img", { });__sveltets_2_ensureAnimation(thing(svelteHTML.mapElementTag('img'),__sveltets_2_AnimationMove,(params)));}
{ svelteHTML.createElement("img", { thing:binding,});}
{ svelteHTML.createElement("img", { "bind:thing":binding,});}
@@ -1,3 +1,3 @@
{ svelteHTML.createElement("input", { });obj.;}
{ svelteHTML.createElement("input", { value:obj.,});}
{ svelteHTML.createElement("input", { "bind:value":obj.,});}
{ const $$_Input0C = __sveltets_2_ensureComponent(Input); new $$_Input0C({ target: __sveltets_2_any(), props: { value:obj.,}});}
Expand Up @@ -4,8 +4,8 @@ async () => { { const $$_div0 = svelteHTML.createElement("div", { });$compile_o
{ const $$_div0 = svelteHTML.createElement("div", { });$compile_options.foo= $$_div0.offsetHeight;}
{ const $$_div0 = svelteHTML.createElement("div", { });$compile_options = $$_div0;}
{ const $$_div0 = svelteHTML.createElement("div", { });$compile_options.foo = $$_div0;}
{ svelteHTML.createElement("div", { noAssignment:$compile_options,});}
{ svelteHTML.createElement("div", { noAssignment:$compile_options.foo,});}};
{ svelteHTML.createElement("div", { "bind:noAssignment":$compile_options,});}
{ svelteHTML.createElement("div", { "bind:noAssignment":$compile_options.foo,});}};
return { props: {}, slots: {}, getters: {}, events: {} }}

export default class Input__SvelteComponent_ extends __sveltets_1_createSvelte2TsxComponent(__sveltets_1_partial(__sveltets_1_with_any_event(render()))) {
Expand Down