Skip to content

Commit f96d9f9

Browse files
authored
Merge pull request #246 from storybookjs/fix-invalid-identifiers
fix: properly transform invalid identifiers
2 parents 93e6758 + 8959105 commit f96d9f9

File tree

5 files changed

+67
-4
lines changed

5 files changed

+67
-4
lines changed

src/compiler/pre-transform/codemods/legacy-story.test.ts

+22
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,28 @@ describe(transformLegacyStory.name, () => {
188188
).toMatchInlineSnapshot(`"<Story name="Default" children={someTemplate} />"`);
189189
});
190190

191+
it("transforms 'template' prop to 'children' and text expression becomes expression tag with identifier to snippet (case with invalid identifier)", async ({
192+
expect,
193+
}) => {
194+
const code = `
195+
<script context="module">
196+
import { Story } from "@storybook/addon-svelte-csf";
197+
</script>
198+
199+
<Story name="Default" template="some template with non valid idenitifier" />
200+
`;
201+
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');
202+
203+
expect(
204+
print(
205+
transformLegacyStory({
206+
component,
207+
state: { componentIdentifierName: {} },
208+
})
209+
)
210+
).toMatchInlineSnapshot(`"<Story name="Default" children={template_c0gseq} />"`);
211+
});
212+
191213
it("when directive 'let:args' is used then it wraps Story fragment with 'children' snippet block", async ({
192214
expect,
193215
}) => {

src/compiler/pre-transform/codemods/legacy-story.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import { camelCase } from 'es-toolkit/string';
2-
31
import {
42
createASTArrayExpression,
53
createASTAttribute,
@@ -11,6 +9,7 @@ import {
119
} from '$lib/parser/ast.js';
1210
import { InvalidTemplateAttribute } from '$lib/utils/error/legacy-api/index.js';
1311

12+
import { hashTemplateName } from '$lib/utils/identifier-utils';
1413
import type { State } from '..';
1514

1615
interface Params {
@@ -253,7 +252,7 @@ function templateToChildren(
253252
value: [
254253
createASTExpressionTag({
255254
type: 'Identifier',
256-
name: camelCase(
255+
name: hashTemplateName(
257256
value[0].type === 'Text'
258257
? value[0].data
259258
: ((value[0].expression as ESTreeAST.Literal).value as string)

src/compiler/pre-transform/codemods/template-to-snippet.test.ts

+21
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,27 @@ describe(transformTemplateToSnippet.name, () => {
4646
`);
4747
});
4848

49+
it("covers a case with provided prop 'id' and prop `id` not being a valid identifier", async ({
50+
expect,
51+
}) => {
52+
const code = `
53+
<script context="module" lang="ts">
54+
import { Template } from "${pkg.name}";
55+
</script>
56+
57+
<Template id="cool-template" let:args>
58+
<Button {...args} variant="primary" />
59+
</Template>
60+
`;
61+
const component = await parseAndExtractSvelteNode<SvelteAST.Component>(code, 'Component');
62+
63+
expect(print(transformTemplateToSnippet({ component }))).toMatchInlineSnapshot(`
64+
"{#snippet template_haitqt(args)}
65+
<Button {...args} variant="primary" />
66+
{/snippet}"
67+
`);
68+
});
69+
4970
it("works with 'let:context' directive", async ({ expect }) => {
5071
const code = `
5172
<script context="module" lang="ts">

src/compiler/pre-transform/codemods/template-to-snippet.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getStringValueFromAttribute } from '$lib/parser/analyse/story/attributes.js';
22
import type { SvelteAST } from '$lib/parser/ast.js';
3+
import { hashTemplateName } from '$lib/utils/identifier-utils';
34

45
interface Params {
56
component: SvelteAST.Component;
@@ -70,7 +71,7 @@ export function transformTemplateToSnippet(params: Params): SvelteAST.SnippetBlo
7071
type: 'SnippetBlock',
7172
expression: {
7273
type: 'Identifier',
73-
name: id ?? 'sb_default_template',
74+
name: id ? hashTemplateName(id) : 'sb_default_template',
7475
},
7576
parameters,
7677
body: fragment,

src/utils/identifier-utils.ts

+20
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,23 @@ export const isValidVariableName = (str: string) => {
5454

5555
return true;
5656
};
57+
58+
/**
59+
* Function to convert a non valid string template name to a valid identifier preventing
60+
* clashing with other templates with similar names.
61+
*
62+
* Stolen with 🧡 from the svelte codebase by @paoloricciuti
63+
*
64+
* @param str the template name
65+
* @returns a hash based on the content of the initial string which is a valid identifier
66+
*/
67+
export function hashTemplateName(str: string) {
68+
if (isValidVariableName(str)) return str;
69+
70+
str = str.replace(/\r/g, '');
71+
let hash = 5381;
72+
let i = str.length;
73+
74+
while (i--) hash = ((hash << 5) - hash) ^ str.charCodeAt(i);
75+
return `template_${(hash >>> 0).toString(36)}`;
76+
}

0 commit comments

Comments
 (0)