Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ export class RenameProviderImpl implements RenameProvider {
}
> = await this.mapAndFilterRenameLocations(renameLocations, docs);

convertedRenameLocations = this.checkShortHandBindingLocation(
convertedRenameLocations = this.checkShortHandBindingOrSlotLetLocation(
lang,
convertedRenameLocations,
docs
Expand Down Expand Up @@ -245,7 +245,7 @@ export class RenameProviderImpl implements RenameProvider {
const replacementsForProp =
lang.findRenameLocations(updatePropLocation.fileName, idx, false, false) || [];

return this.checkShortHandBindingLocation(
return this.checkShortHandBindingOrSlotLetLocation(
lang,
await this.mapAndFilterRenameLocations(replacementsForProp, fragments),
fragments
Expand Down Expand Up @@ -382,7 +382,7 @@ export class RenameProviderImpl implements RenameProvider {
return this.lsAndTsDocResolver.getSnapshot(filePath);
}

private checkShortHandBindingLocation(
private checkShortHandBindingOrSlotLetLocation(
lang: ts.LanguageService,
renameLocations: Array<ts.RenameLocation & { range: Range }>,
fragments: SnapshotFragmentMap
Expand All @@ -408,40 +408,29 @@ export class RenameProviderImpl implements RenameProvider {

const { originalText } = fragment;

const possibleJsxAttribute = findContainingNode(
sourceFile,
location.textSpan,
ts.isJsxAttribute
);
if (!possibleJsxAttribute) {
return location;
}

const attributeName = possibleJsxAttribute.name.getText();
const { initializer } = possibleJsxAttribute;
const renamingInfo =
this.getShorthandPropInfo(sourceFile, location) ??
this.getSlotLetInfo(sourceFile, location);

// not props={props}
if (
!initializer ||
!ts.isJsxExpression(initializer) ||
attributeName !== initializer.expression?.getText()
) {
if (!renamingInfo) {
return location;
}

const [renamingNode, identifierName] = renamingInfo;

const originalStart = offsetAt(location.range.start, originalText);

const isShortHandBinding =
originalText.substr(originalStart - bind.length, bind.length) === bind;

const directiveName = (isShortHandBinding ? bind : '') + attributeName;
const directiveName = (isShortHandBinding ? bind : '') + identifierName;
const prefixText = directiveName + '={';

const newRange = mapRangeToOriginal(
fragment,
convertRange(fragment, {
start: possibleJsxAttribute.getStart(),
length: possibleJsxAttribute.getWidth()
start: renamingNode.getStart(),
length: renamingNode.getWidth()
})
);

Expand All @@ -463,6 +452,62 @@ export class RenameProviderImpl implements RenameProvider {
};
});
}

private getShorthandPropInfo(
sourceFile: ts.SourceFile,
location: ts.RenameLocation
): [ts.Node, string] | null {
const possibleJsxAttribute = findContainingNode(
sourceFile,
location.textSpan,
ts.isJsxAttribute
);
if (!possibleJsxAttribute) {
return null;
}

const attributeName = possibleJsxAttribute.name.getText();
const { initializer } = possibleJsxAttribute;

// not props={props}
if (
!initializer ||
!ts.isJsxExpression(initializer) ||
attributeName !== initializer.expression?.getText()
) {
return null;
}

return [possibleJsxAttribute, attributeName];
}

private getSlotLetInfo(
sourceFile: ts.SourceFile,
location: ts.RenameLocation
): [ts.Node, string] | null {
const possibleSlotLet = findContainingNode(
sourceFile,
location.textSpan,
ts.isVariableDeclaration
);
if (!possibleSlotLet || !ts.isObjectBindingPattern(possibleSlotLet.name)) {
return null;
}

const bindingElement = findContainingNode(
possibleSlotLet.name,
location.textSpan,
ts.isBindingElement
);

if (!bindingElement || bindingElement.propertyName) {
return null;
}

const identifierName = bindingElement.name.getText();

return [bindingElement, identifierName];
}
}

function unique<T>(array: T[]): T[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('RenameProvider', () => {
const renameDocSlotEventsImporter = await openDoc('rename-slot-events-importer.svelte');
const renameDocPropWithSlotEvents = await openDoc('rename-prop-with-slot-events.svelte');
const renameDocShorthand = await openDoc('rename-shorthand.svelte');
const renameSlotLet = await openDoc('rename-slot-let.svelte');

return {
provider,
Expand All @@ -52,6 +53,7 @@ describe('RenameProvider', () => {
renameDocSlotEventsImporter,
renameDocPropWithSlotEvents,
renameDocShorthand,
renameSlotLet,
docManager
};

Expand Down Expand Up @@ -599,7 +601,7 @@ describe('RenameProvider', () => {
});
});

it('should can rename shorthand props without breaking value-passing', async () => {
it('can rename shorthand props without breaking value-passing', async () => {
const { provider, renameDocShorthand } = await setup();

const result = await provider.rename(renameDocShorthand, Position.create(3, 16), 'newName');
Expand Down Expand Up @@ -676,4 +678,43 @@ describe('RenameProvider', () => {
}
});
});

it('can rename slot let to an alias', async () => {
const { provider, renameSlotLet } = await setup();

const result = await provider.rename(renameSlotLet, Position.create(4, 7), 'newName');

assert.deepStrictEqual(result, {
changes: {
[getUri('rename-slot-let.svelte')]: [
{
newText: 'aSlot={newName}',
range: {
end: {
character: 12,
line: 4
},
start: {
character: 7,
line: 4
}
}
},
{
newText: 'newName',
range: {
end: {
character: 26,
line: 4
},
start: {
character: 21,
line: 4
}
}
}
]
}
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<script>
import A from './rename-prop-with-slot-events.svelte';
</script>

<A let:aSlot let:hi>{aSlot}</A>
6 changes: 3 additions & 3 deletions packages/svelte2tsx/src/htmlxtojsx/nodes/slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ export function handleSlot(
str.move(attr.start, attr.end, slotDefInsertionPoint);

//remove let:
str.remove(attr.start, attr.start + 'let:'.length);
if (hasMoved) {
str.overwrite(attr.start, attr.start + 'let:'.length, ', ');
} else {
str.remove(attr.start, attr.start + 'let:'.length);
str.appendRight(attr.start + 'let:'.length, ', ');
}

templateScope.inits.add(attr.expression?.name || attr.name);
hasMoved = true;
if (attr.expression) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<Component let:hi let:hi2={hi_2} let:hi3>
{hi}{hi_2}{hi3}
</Component>
23 changes: 23 additions & 0 deletions packages/svelte2tsx/test/sourcemaps/samples/slot-let/mappings.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
///<reference types="svelte" />
<></>;function render() { {/**
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
<><Component >{() => { let {hi, hi2:hi_2, hi3} = /*Ωignore_startΩ*/new Component({target: __sveltets_1_any(''), props: {}})/*Ωignore_endΩ*/.$$slot_def['default'];<>{/**
=# Originless mappings
<><Component•••>{()•=>•{•let•{hi,•hi2:hi_2,•hi3}•=•/*Ωignore_startΩ*/new•Component({target:•__sveltets_1_any(''),•props:•{}})/*Ωignore_endΩ*/.$$slot_def['default'];<>↲ [generated] line 3
<Component•••> hi hi2=hi_2 hi3 ↲
#============== Order-breaking mappings
<Component• hi• hi2= hi_2 • hi3>↲
<Component•let:hi•let:hi2={hi_2}•let:hi3>↲ [original] line 1
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
{hi}{hi_2}{hi3} {/**
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
</>}}</Component></> {/**
====# Originless mappings
</>}}</Component></>↲ [generated] line 5
</Component>
</Component> [original] line 3
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
return { props: {}, slots: {}, getters: {}, events: {} }}

export default class Input__SvelteComponent_ extends __sveltets_1_createSvelte2TsxComponent(__sveltets_1_partial(__sveltets_1_with_any_event(render()))) {
}
8 changes: 8 additions & 0 deletions packages/svelte2tsx/test/sourcemaps/samples/slot-let/test.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/** tested-ranges: [[15,2,"hi"],[22,9,"hi2:hi_2"],[37,3,"hi3"]] */ {/**
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
<><Component >{() => { let {hi, hi2:hi_2, hi3} = /*Ωignore_startΩ*/new Component({target: __sveltets_1_any(''), props: {}})/*Ωignore_endΩ*/.$$slot_def['default'];<>{/**
1= 2======= 3== [generated] line 3 */}
<Component let:hi let:hi2={hi_2} let:hi3> {/**
1= 2======== 3== [original] line 1
------------------------------------------------------------------------------------------------------------------------------------------------------ */}
/** origin-hash: 1ljhdpn */