Skip to content

Commit

Permalink
Implement a Svelte StoryIndexer (#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
JReinhold authored Oct 27, 2022
2 parents 34c52d0 + 9b95d2b commit 29e1060
Show file tree
Hide file tree
Showing 10 changed files with 3,700 additions and 2,926 deletions.
3 changes: 3 additions & 0 deletions .babelrc.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
module.exports = {
presets: ['@babel/preset-env', '@babel/preset-typescript'],
plugins: [
"@babel/plugin-transform-runtime"
],
env: {
esm: {
presets: [
Expand Down
27 changes: 27 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const path = require("path");

module.exports = {
framework: '@storybook/svelte-webpack5',
stories: [
"../stories/**/*.stories.svelte",
],
Expand All @@ -7,4 +10,28 @@ module.exports = {
"@storybook/addon-essentials",
"@storybook/addon-interactions",
],
webpackFinal: async (config, { configType }) => {
// `configType` has a value of 'DEVELOPMENT' or 'PRODUCTION'
// You can change the configuration based on that.
// 'PRODUCTION' is used when building the static version of storybook.

config.resolve = {
...config.resolve,
alias: {
svelte: path.resolve("node_modules", "svelte"),
},
};

config.module.rules = [ {
resourceQuery: /raw/,
type: 'asset/source'
}, ...config.module.rules]

config.module.rules
.filter(r => r.test && r.test.toString().includes("svelte"))
.forEach(r => r.resourceQuery = { not: [/raw/] });

// Return the altered config
return config;
},
};
25 changes: 16 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
"prepublish": "yarn clean && yarn build",
"test": "jest",
"lint": "eslint --cache --cache-location=.cache/eslint --ext .js,.jsx,.json,.html,.ts,.tsx,.mjs --report-unused-disable-directives",
"storybook": "start-storybook -p 6006",
"storybook": "sb dev -p 6006",
"start": "concurrently \"yarn storybook -- --no-manager-cache --quiet\" \"yarn build -- --watch\"",
"build-storybook": "build-storybook",
"build-storybook": "sb build",
"release": "yarn build && auto shipit"
},
"dependencies": {
"ts-dedent": "^2.0.0"
"ts-dedent": "^2.0.0",
"@babel/runtime": "^7.17.5"
},
"devDependencies": {
"@auto-it/released": "^10.32.6",
Expand All @@ -43,13 +44,18 @@
"@babel/preset-env": "^7.16.11",
"@babel/preset-react": "^7.16.7",
"@babel/preset-typescript": "^7.16.7",
"@storybook/addon-essentials": "^6.4.20",
"@storybook/addon-interactions": "^6.4.20",
"@babel/plugin-transform-runtime": "^7.17.5",
"@storybook/addon-essentials": "^7.0.0-alpha.29",
"@storybook/addon-interactions": "^7.0.0-alpha.29",
"@storybook/jest": "^0.0.10",
"@storybook/testing-library": "^0.0.9",
"@storybook/addon-storyshots": "^6.4.20",
"@storybook/addon-storyshots": "^7.0.0-alpha.29",
"@storybook/eslint-config-storybook": "^3.1.2",
"@storybook/svelte": "^6.4.20",
"@storybook/svelte": "^7.0.0-alpha.29",
"@storybook/core-server": "^7.0.0-alpha.29",
"@storybook/core-client": "^7.0.0-alpha.29",
"@storybook/svelte-webpack5": "7.0.0-alpha.29",
"sb": "7.0.0-alpha.29",
"auto": "^10.3.0",
"babel-jest": "^29.1.0",
"babel-loader": "^8.1.0",
Expand All @@ -62,14 +68,15 @@
"react": "^17.0.1",
"react-dom": "^17.0.1",
"rimraf": "^3.0.2",
"svelte": "^3.46.6",
"svelte": "^3.50.0",
"svelte-jester": "^2.3.2",
"svelte-loader": "^3.1.2",
"typescript": "^3.9.7"
},
"peerDependencies": {
"@storybook/svelte": ">=6.4.20",
"svelte": "^3.46.6",
"@storybook/theming": ">=6.4.20",
"svelte": "^3.50.0",
"svelte-loader": "^3.1.2"
},
"peerDependenciesMeta": {
Expand Down
4 changes: 4 additions & 0 deletions src/parser/extract-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ export function extractId(
return id;
}

if (!name) {
throw new Error("Id or Name should be specified");
}

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}`);
Expand Down
20 changes: 20 additions & 0 deletions src/parser/extract-stories.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ describe('extractSource', () => {
"default",
"Story",
],
"meta": Object {},
"stories": Object {
"MyStory": Object {
"hasArgs": false,
"name": "MyStory",
"source": "<div>a story</div>",
"storyId": "mystory--my-story",
"template": false,
},
},
Expand All @@ -46,11 +48,13 @@ describe('extractSource', () => {
"default",
"Story",
],
"meta": Object {},
"stories": Object {
"myId": Object {
"hasArgs": false,
"name": "MyStory",
"source": "<div>a story</div>",
"storyId": "myid--my-id",
"template": false,
},
},
Expand All @@ -74,11 +78,13 @@ describe('extractSource', () => {
"default",
"Story",
],
"meta": Object {},
"stories": Object {
"MyStory": Object {
"hasArgs": true,
"name": "MyStory",
"source": "<div>a story</div>",
"storyId": "mystory--my-story",
"template": false,
},
},
Expand All @@ -102,11 +108,13 @@ describe('extractSource', () => {
"default",
"Template",
],
"meta": Object {},
"stories": Object {
"tpl:MyTemplate": Object {
"hasArgs": false,
"name": "MyTemplate",
"source": "<div>a template</div>",
"storyId": "mytemplate--my-template",
"template": true,
},
},
Expand All @@ -130,11 +138,13 @@ describe('extractSource', () => {
"default",
"Template",
],
"meta": Object {},
"stories": Object {
"tpl:default": Object {
"hasArgs": false,
"name": "default",
"source": "<div>a template</div>",
"storyId": "default--default",
"template": true,
},
},
Expand All @@ -161,17 +171,20 @@ describe('extractSource', () => {
"default",
"Template",
],
"meta": Object {},
"stories": Object {
"Story1": Object {
"hasArgs": false,
"name": "Story1",
"source": "<div>story 1</div>",
"storyId": "story1--story-1",
"template": false,
},
"Story2": Object {
"hasArgs": false,
"name": "Story2",
"source": "<div>story 2</div>",
"storyId": "story2--story-2",
"template": false,
},
},
Expand All @@ -198,11 +211,16 @@ describe('extractSource', () => {
"SBStory",
"SBMeta",
],
"meta": Object {
"id": undefined,
"title": "test",
},
"stories": Object {
"Story1": Object {
"hasArgs": false,
"name": "Story1",
"source": "<div>story 1</div>",
"storyId": "test--story-1",
"template": false,
},
},
Expand All @@ -228,11 +246,13 @@ describe('extractSource', () => {
"Story",
"Button",
],
"meta": Object {},
"stories": Object {
"Button77471352": Object {
"hasArgs": false,
"name": "Button",
"source": "<div>a story</div>",
"storyId": "button77471352--button-77471352",
"template": false,
},
},
Expand Down
78 changes: 49 additions & 29 deletions src/parser/extract-stories.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import dedent from 'ts-dedent';
import * as svelte from 'svelte/compiler';
import { extractId } from './extract-id';

import { toId, storyNameFromExport } from "@storybook/csf";
interface StoryDef {
storyId: string;
name: string;
template: boolean;
source: string;
hasArgs: boolean;
}

interface MetaDef {
title?: string;
id?: string;
}

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

function getStaticAttribute(name: string, node: any): string {
function getStaticAttribute(name: string, node: any): string|undefined {
// extract the attribute
const attribute = node.attributes.find(
(att: any) => att.type === 'Attribute' && att.name === name
);

if (!attribute) {
return null;
return undefined;
}

const { value } = attribute;
Expand All @@ -47,37 +54,41 @@ export function extractStories(component: string): StoriesDef {
const localNames = {
Story: 'Story',
Template: 'Template',
Meta: 'Meta',
};

svelte.walk(ast.instance, {
enter(node: any) {
if (node.type === 'ImportDeclaration') {
if (node.source.value === '@storybook/addon-svelte-csf') {
node.specifiers
.filter((n: any) => n.type === 'ImportSpecifier')
.forEach((n: any) => {
localNames[n.imported.name] = n.local.name;
});
}
if (ast.instance) {
svelte.walk(ast.instance, {
enter(node: any) {
if (node.type === 'ImportDeclaration') {
if (node.source.value === '@storybook/addon-svelte-csf') {
node.specifiers
.filter((n: any) => n.type === 'ImportSpecifier')
.forEach((n: any) => {
localNames[n.imported.name] = n.local.name;
});
}

this.skip();
}
},
});
this.skip();
}
},
});

// 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();
}
},
});
// 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> = {};
const meta: MetaDef = {};
svelte.walk(ast.html, {
enter(node: any) {
if (
Expand Down Expand Up @@ -113,19 +124,28 @@ export function extractStories(component: string): StoriesDef {

source = dedent(component.substr(start, end - start));
}

stories[isTemplate ? `tpl:${id}` : id] = {
storyId: toId(meta.id || meta.title || id, storyNameFromExport(id)),
name,
template: isTemplate,
source,
hasArgs: node.attributes.find((att: any) => att.type === 'Let') != null,
};
}
} else if (
node.type === 'InlineComponent' &&
(node.name === localNames.Meta)
) {
this.skip();

meta.title = getStaticAttribute("title", node);
meta.id = getStaticAttribute("id", node);
}
},
});

return {
meta,
stories,
allocatedIds,
};
Expand Down
13 changes: 13 additions & 0 deletions src/preset/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { svelteIndexer } from "./indexer.js";

export function managerEntries(entry = []) {
return [...entry, require.resolve('./manager')];
}
Expand All @@ -22,3 +24,14 @@ export function webpack(config) {
},
};
}

export const storyIndexers = async (indexers) => {

return [
{
test: /\.stories\.svelte$/,
indexer: svelteIndexer,
},
...(indexers || []),
];
}
Loading

0 comments on commit 29e1060

Please sign in to comment.