Skip to content

Commit

Permalink
Fix duplicated exported id
Browse files Browse the repository at this point in the history
  • Loading branch information
j3rem1e committed Apr 6, 2021
1 parent 1c88870 commit 59274fe
Show file tree
Hide file tree
Showing 9 changed files with 220 additions and 54 deletions.
4 changes: 2 additions & 2 deletions src/parser/collect-stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const createFragment = document.createDocumentFragment
? () => document.createDocumentFragment()
: () => document.createElement('div');

export default (StoriesComponent, stories) => {
export default (StoriesComponent, { stories = {}, allocatedIds }) => {
const repositories = {
meta: null,
stories: [],
Expand Down Expand Up @@ -72,7 +72,7 @@ export default (StoriesComponent, stories) => {
.reduce((all, story) => {
const { id, name, template, component, source = false, ...props } = story;

const storyId = extractId(story);
const storyId = extractId(story, allocatedIds);
if (!storyId) {
return all;
}
Expand Down
2 changes: 1 addition & 1 deletion src/parser/collect-stories.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import TestStories from '../components/__tests__/TestStories.svelte';

describe('parse-stories', () => {
test('Extract Stories', () => {
const data = collectStories(TestStories, { 'tpl:tpl2': 'tpl2src' });
const data = collectStories(TestStories, { stories: { 'tpl:tpl2': 'tpl2src' } });
const { stories, meta } = data;
expect(meta).toMatchInlineSnapshot(`
Object {
Expand Down
3 changes: 3 additions & 0 deletions src/parser/extract-id.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ describe('extract-id', () => {
test('name with spaces', () => {
expect(extractId({ name: 'Name with spaces' })).toBe('NameWithSpaces');
});
test('duplicates id', () => {
expect(extractId({ name: 'Button' }, ['Button'])).toBe('Button77471352');
});
});
27 changes: 25 additions & 2 deletions src/parser/extract-id.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,31 @@
import { logger } from '@storybook/client-logger';

// extract a story id
export function extractId({ id, name }: { id?: string; name?: string }): string {
export function extractId(
{
id,
name,
}: {
id?: string;
name?: string;
},
allocatedIds: string[] = []
): string {
if (id) {
return id;
}

return name.replace(/\W+(.)/g, (_, chr) => chr.toUpperCase());
let generated = name.replace(/\W+(.)/g, (_, chr) => chr.toUpperCase());
if (allocatedIds.indexOf(generated) >= 0) {
logger.warn(`Story name conflict with exports - Please add an explicit id for story ${name}`);
generated += hashCode(name);
}
return generated;
}

function hashCode(str: string): string {
return str
.split('')
.reduce((prevHash, currVal) => ((prevHash << 5) - prevHash + currVal.charCodeAt(0)) | 0, 0) // eslint-disable-line
.toString(16);
}
155 changes: 114 additions & 41 deletions src/parser/extract-stories.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,17 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"MyStory": Object {
"hasArgs": false,
"name": "MyStory",
"source": "<div>a story</div>",
"template": false,
"allocatedIds": Array [
"default",
"Story",
],
"stories": Object {
"MyStory": Object {
"hasArgs": false,
"name": "MyStory",
"source": "<div>a story</div>",
"template": false,
},
},
}
`);
Expand All @@ -36,11 +42,17 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"myId": Object {
"hasArgs": false,
"name": "MyStory",
"source": "<div>a story</div>",
"template": false,
"allocatedIds": Array [
"default",
"Story",
],
"stories": Object {
"myId": Object {
"hasArgs": false,
"name": "MyStory",
"source": "<div>a story</div>",
"template": false,
},
},
}
`);
Expand All @@ -58,11 +70,17 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"MyStory": Object {
"hasArgs": true,
"name": "MyStory",
"source": "<div>a story</div>",
"template": false,
"allocatedIds": Array [
"default",
"Story",
],
"stories": Object {
"MyStory": Object {
"hasArgs": true,
"name": "MyStory",
"source": "<div>a story</div>",
"template": false,
},
},
}
`);
Expand All @@ -80,11 +98,17 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"tpl:MyTemplate": Object {
"hasArgs": false,
"name": "MyTemplate",
"source": "<div>a template</div>",
"template": true,
"allocatedIds": Array [
"default",
"Template",
],
"stories": Object {
"tpl:MyTemplate": Object {
"hasArgs": false,
"name": "MyTemplate",
"source": "<div>a template</div>",
"template": true,
},
},
}
`);
Expand All @@ -102,11 +126,17 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"tpl:default": Object {
"hasArgs": false,
"name": "default",
"source": "<div>a template</div>",
"template": true,
"allocatedIds": Array [
"default",
"Template",
],
"stories": Object {
"tpl:default": Object {
"hasArgs": false,
"name": "default",
"source": "<div>a template</div>",
"template": true,
},
},
}
`);
Expand All @@ -127,17 +157,23 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"Story1": Object {
"hasArgs": false,
"name": "Story1",
"source": "<div>story 1</div>",
"template": false,
},
"Story2": Object {
"hasArgs": false,
"name": "Story2",
"source": "<div>story 2</div>",
"template": false,
"allocatedIds": Array [
"default",
"Template",
],
"stories": Object {
"Story1": Object {
"hasArgs": false,
"name": "Story1",
"source": "<div>story 1</div>",
"template": false,
},
"Story2": Object {
"hasArgs": false,
"name": "Story2",
"source": "<div>story 2</div>",
"template": false,
},
},
}
`);
Expand All @@ -157,11 +193,48 @@ describe('extractSource', () => {
`)
).toMatchInlineSnapshot(`
Object {
"Story1": Object {
"hasArgs": false,
"name": "Story1",
"source": "<div>story 1</div>",
"template": false,
"allocatedIds": Array [
"default",
"SBStory",
"SBMeta",
],
"stories": Object {
"Story1": Object {
"hasArgs": false,
"name": "Story1",
"source": "<div>story 1</div>",
"template": false,
},
},
}
`);
});
test('Duplicate Id', () => {
expect(
extractStories(`
<script>
import { Story } from '@storybook/svelte';
import Button from './Button.svelte';
</script>
<Story name="Button">
<div>a story</div>
</Story>
`)
).toMatchInlineSnapshot(`
Object {
"allocatedIds": Array [
"default",
"Story",
"Button",
],
"stories": Object {
"Button77471352": Object {
"hasArgs": false,
"name": "Button",
"source": "<div>a story</div>",
"template": false,
},
},
}
`);
Expand Down
37 changes: 31 additions & 6 deletions src/parser/extract-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ interface StoryDef {
hasArgs: boolean;
}

interface StoriesDef {
stories: Record<string, StoryDef>;
allocatedIds: string[];
}

function getStaticAttribute(name: string, node: any): string {
// extract the attribute
const attribute = node.attributes.find(
Expand All @@ -33,10 +38,12 @@ function getStaticAttribute(name: string, node: any): string {
* @param component Component Source
* @returns Map of storyName -> source
*/
export function extractStories(component: string): Record<string, StoryDef> {
export function extractStories(component: string): StoriesDef {
// compile
const { ast } = svelte.compile(component);

const allocatedIds: string[] = ['default'];

const localNames = {
Story: 'Story',
Template: 'Template',
Expand All @@ -58,6 +65,18 @@ export function extractStories(component: string): Record<string, StoryDef> {
},
});

// extracts allocated Ids
svelte.walk(ast.instance, {
enter(node: any) {
if (node.type === 'ImportDeclaration') {
node.specifiers
.map((n: any) => n.local.name)
.forEach((name: string) => allocatedIds.push(name));
this.skip();
}
},
});

const stories: Record<string, StoryDef> = {};
svelte.walk(ast.html, {
enter(node: any) {
Expand All @@ -77,10 +96,13 @@ export function extractStories(component: string): Record<string, StoryDef> {
name = 'default';
}

const id = extractId({
id: getStaticAttribute('id', node),
name,
});
const id = extractId(
{
id: getStaticAttribute('id', node),
name,
},
isTemplate ? undefined : allocatedIds
);

if (name && id) {
// ignore stories without children
Expand All @@ -103,5 +125,8 @@ export function extractStories(component: string): Record<string, StoryDef> {
},
});

return stories;
return {
stories,
allocatedIds,
};
}
5 changes: 3 additions & 2 deletions src/parser/svelte-stories-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ function transformSvelteStories(code: string) {

const source = readFileSync(resource).toString();

const stories = extractStories(source);
const storiesDef = extractStories(source);
const { stories } = storiesDef;

const storyDef = Object.entries(stories)
.filter(([, def]) => !def.template)
Expand All @@ -55,7 +56,7 @@ function transformSvelteStories(code: string) {

return dedent`${codeWithoutDefaultExport}
const { default: parser } = require('${parser}');
const __storiesMetaData = parser(${componentName}, ${JSON.stringify(stories)});
const __storiesMetaData = parser(${componentName}, ${JSON.stringify(storiesDef)});
export default __storiesMetaData.meta;
${storyDef};
`;
Expand Down
Loading

0 comments on commit 59274fe

Please sign in to comment.