Socket
Socket
Sign inDemoInstall

@storybook/addon-svelte-csf

Package Overview
Dependencies
Maintainers
11
Versions
279
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@storybook/addon-svelte-csf - npm Package Compare versions

Comparing version 5.0.0--canary.181.ef3ca0a.0 to 5.0.0--canary.181.efeeaf8.0

dist/compiler/transform/appendix/create-code-by-story-map.d.ts

90

dist/compiler/plugin.js

@@ -17,3 +17,3 @@ /**

import { destructureMetaFromDefineMeta } from './transform/define-meta/destructure-meta.js';
import { insertStoryHTMLCommentAsDescription } from './transform/Story/description.js';
import { updateCompiledStoryProps } from './transform/compiled-story-props.js';
import { getSvelteAST } from '../parser/ast.js';

@@ -23,3 +23,2 @@ import { extractStoriesNodesFromExportDefaultFn } from '../parser/extract/compiled/stories.js';

import { extractSvelteASTNodes } from '../parser/extract/svelte/nodes.js';
import { getNameFromFilename } from '../utils/get-component-name.js';
export async function plugin() {

@@ -36,77 +35,74 @@ const [{ createFilter }, { loadSvelteConfig }] = await Promise.all([

enforce: 'post',
async transform(code_, filename) {
if (!filter(filename))
async transform(compiledCode, id) {
if (!filter(id))
return undefined;
const compiledAST = this.parse(code_);
let code = new MagicString(code_);
const componentName = getNameFromFilename(filename);
if (!componentName) {
// TODO: make error message more user friendly
// what happened, how to fix
throw new Error(`Failed to extract component name from filename: ${filename}`);
}
let source = fs.readFileSync(filename).toString();
const compiledAST = this.parse(compiledCode);
let magicCompiledCode = new MagicString(compiledCode);
// @ts-expect-error FIXME: `this.originalCode` exists at runtime.
// Need to research if its documented somewhere
let rawCode = this.originalCode ?? fs.readFileSync(id).toString();
if (svelteConfig?.preprocess) {
const processed = await preprocess(source.toString(), svelteConfig.preprocess, {
filename: filename,
const processed = await preprocess(rawCode, svelteConfig.preprocess, {
filename: id,
});
source = processed.code;
rawCode = processed.code;
}
const svelteAST = getSvelteAST({ source, filename });
const svelteNodes = await extractSvelteASTNodes({
const svelteAST = getSvelteAST({ code: rawCode, filename: id });
const svelteASTNodes = await extractSvelteASTNodes({
ast: svelteAST,
filename,
filename: id,
});
const compiledNodes = await extractCompiledASTNodes({
ast: compiledAST,
filename,
filename: id,
});
const extractedCompiledStoriesNodes = await extractStoriesNodesFromExportDefaultFn({
nodes: compiledNodes,
filename,
filename: id,
});
// WARN:
// IMPORTANT! The plugins starts updating the compiled ouput code from the bottom.
// Why? Because once we start updating nodes in the stringified output from the top,
// then other nodes `start` and `end` numbers will not be correct anymore.
// Hence the reason why reversing both arrays with stories _(svelte and compiled)_.
const svelteStories = [...svelteNodes.storyComponents].reverse();
/*
* WARN:
* IMPORTANT! The plugins starts updating the compiled output code from the bottom.
* Why? Because once we start updating nodes in the stringified output from the top,
* then other nodes' `start` and `end` numbers will not be correct anymore.
* Hence the reason why reversing both arrays with stories _(svelte and compiled)_.
*/
const svelteStories = [...svelteASTNodes.storyComponents].reverse();
const compiledStories = [...extractedCompiledStoriesNodes].reverse();
for (const [index, compiled] of Object.entries(compiledStories)) {
insertStoryHTMLCommentAsDescription({
code,
nodes: { svelte: svelteStories[index], compiled },
filename,
});
}
await Promise.all(compiledStories.map((compiled, index) => updateCompiledStoryProps({
code: magicCompiledCode,
componentASTNodes: { svelte: svelteStories[index], compiled },
svelteASTNodes,
filename: id,
originalCode: rawCode,
})));
await destructureMetaFromDefineMeta({
code,
code: magicCompiledCode,
nodes: compiledNodes,
filename,
filename: id,
});
insertDefineMetaJSDocCommentAsDescription({
code,
code: magicCompiledCode,
nodes: {
compiled: compiledNodes,
svelte: svelteNodes,
svelte: svelteASTNodes,
},
filename,
filename: id,
});
removeExportDefault({
code,
code: magicCompiledCode,
nodes: compiledNodes,
filename,
filename: id,
});
await createAppendix({
componentName,
code,
code: magicCompiledCode,
nodes: {
compiled: compiledNodes,
svelte: svelteNodes,
svelte: svelteASTNodes,
},
filename,
filename: id,
});
return {
code: code.toString(),
map: code.generateMap({ hires: true, source: filename }),
code: magicCompiledCode.toString(),
map: magicCompiledCode.generateMap({ hires: true, source: id }),
};

@@ -113,0 +109,0 @@ },

@@ -1,6 +0,7 @@

import type { VariableDeclaration } from 'estree';
import type { FunctionDeclaration, VariableDeclaration } from 'estree';
import type { getMetaIdentifier } from '../../../parser/analyse/meta/identifier.js';
interface Params {
componentName: string;
storiesFunctionDeclaration: FunctionDeclaration;
metaIdentifier: ReturnType<typeof getMetaIdentifier>;
codeByStoryMapDeclaration: VariableDeclaration;
filename: string;

@@ -7,0 +8,0 @@ }

export function createVariableFromRuntimeStoriesCall(params) {
const { componentName, metaIdentifier } = params;
const { storiesFunctionDeclaration, metaIdentifier, codeByStoryMapDeclaration } = params;
return {

@@ -24,5 +24,9 @@ type: 'VariableDeclaration',

type: 'Identifier',
name: componentName,
name: storiesFunctionDeclaration.id.name,
},
metaIdentifier,
{
type: 'Identifier',
name: codeByStoryMapDeclaration.declarations[0].id.name
}
],

@@ -29,0 +33,0 @@ optional: false,

@@ -5,3 +5,2 @@ import MagicString from 'magic-string';

interface Params {
componentName: string;
code: MagicString;

@@ -8,0 +7,0 @@ nodes: {

import MagicString from 'magic-string';
import { toJs } from 'estree-util-to-js';
import { createExportDefaultMeta } from './appendix/create-export-default.js';
import { createCodeByStoryMap } from './appendix/create-code-by-story-map.js';
import { createExportOrderVariable } from './appendix/create-export-order.js';

@@ -11,5 +12,5 @@ import { createImport } from './appendix/create-import.js';

export async function createAppendix(params) {
const { componentName, code, nodes, filename } = params;
const { code, nodes, filename } = params;
const { compiled, svelte } = nodes;
const { defineMetaVariableDeclaration } = compiled;
const { defineMetaVariableDeclaration, storiesFunctionDeclaration } = compiled;
const storyIdentifiers = await getStoriesIdentifiers({ nodes: svelte, filename });

@@ -20,5 +21,7 @@ const metaIdentifier = getMetaIdentifier({

});
const codeByStoryMapDeclaration = createCodeByStoryMap({ storyIdentifiers });
const variableFromRuntimeStoriesCall = createVariableFromRuntimeStoriesCall({
componentName,
storiesFunctionDeclaration,
metaIdentifier,
codeByStoryMapDeclaration,
filename,

@@ -36,2 +39,3 @@ });

createImport(),
createCodeByStoryMap({ storyIdentifiers }),
variableFromRuntimeStoriesCall,

@@ -38,0 +42,0 @@ createExportDefaultMeta({ metaIdentifier, filename }),

import type { Meta, StoryContext as BaseStoryContext, StoryObj } from '@storybook/svelte';
import type { ComponentType } from 'svelte';
import Story from './runtime/Story.svelte';

@@ -9,3 +10,3 @@ export type { Story };

};
export type Args<TStory extends ReturnType<typeof defineMeta>['Story']> = TStory extends typeof Story<infer TMeta extends Meta> ? StoryObj<TMeta>['args'] : never;
export type StoryContext<TStory extends ReturnType<typeof defineMeta>['Story']> = TStory extends typeof Story<infer TMeta extends Meta> ? BaseStoryContext<StoryObj<TMeta>['args']> : never;
export type Args<TStory extends ComponentType> = TStory extends typeof Story<infer TMeta extends Meta> ? StoryObj<TMeta>['args'] : never;
export type StoryContext<TStory extends ComponentType> = TStory extends typeof Story<infer TMeta extends Meta> ? BaseStoryContext<StoryObj<TMeta>['args']> : never;

@@ -14,3 +14,3 @@ import fs from 'node:fs/promises';

createIndex: async (filename, { makeTitle }) => {
let [source, { loadSvelteConfig }] = await Promise.all([
let [code, { loadSvelteConfig }] = await Promise.all([
fs.readFile(filename, { encoding: 'utf8' }),

@@ -21,7 +21,7 @@ import('@sveltejs/vite-plugin-svelte'),

if (svelteConfig?.preprocess) {
source = (await preprocess(source, svelteConfig.preprocess, {
code = (await preprocess(code, svelteConfig.preprocess, {
filename: filename,
})).code;
}
const svelteAST = getSvelteAST({ source, filename });
const svelteAST = getSvelteAST({ code, filename });
const nodes = await extractSvelteASTNodes({ ast: svelteAST, filename });

@@ -28,0 +28,0 @@ const metaPropertiesNodes = extractMetaPropertiesNodes({

@@ -9,3 +9,3 @@ import { describe, expect, it } from 'vitest';

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -31,3 +31,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -53,3 +53,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -76,3 +76,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -100,3 +100,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -122,3 +122,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -144,3 +144,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -173,3 +173,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -209,3 +209,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -233,3 +233,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -236,0 +236,0 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

@@ -9,3 +9,3 @@ import { describe, expect, it } from 'vitest';

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -30,3 +30,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -33,0 +33,0 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

/// <reference types="svelte" />
import { type Root } from 'svelte/compiler';
interface GetSvelteASTOptions {
source: string;
code: string;
filename?: string;

@@ -6,0 +6,0 @@ }

import { compile } from 'svelte/compiler';
export function getSvelteAST(options) {
const { filename, source } = options;
const { ast } = compile(source, {
const { filename, code } = options;
const { ast } = compile(code, {
filename,

@@ -6,0 +6,0 @@ modernAst: true,

@@ -13,3 +13,3 @@ import pkg from '@storybook/addon-svelte-csf/package.json' with { type: 'json' };

const { ast, filename } = params;
const state = {};
const state = { potentialStoriesFunctionDeclaration: [] };
const visitors = {

@@ -51,19 +51,22 @@ ImportDeclaration(node, { state, visit }) {

},
FunctionDeclaration(node, { state }) {
state.potentialStoriesFunctionDeclaration.push(node);
},
ExportDefaultDeclaration(node, { state }) {
state.exportDefault = node;
// WARN: This may be confusing.
// In the `NODE_ENV="production"` the export default is different.
// Identifier to a FunctionDeclaration.
if (process.env.NODE_ENV === 'production' &&
node.declaration.type === 'FunctionDeclaration' &&
isStoriesComponentFn(node.declaration)) {
if (node.declaration.type === 'FunctionDeclaration') {
/*
In production, Svelte will compile the component to:
export default COMPONENT_NAME () {...}
*/
state.storiesFunctionDeclaration = node.declaration;
}
},
FunctionDeclaration(node, { state }) {
// WARN: This may be confusing.
// In the `NODE_ENV="development"` the export default is different.
// A `FunctionDeclaration`
if (isStoriesComponentFn(node)) {
state.storiesFunctionDeclaration = node;
else if (node.declaration.type === 'Identifier') {
/*
In development, Svelte will compile the component to:
function COMPONENT_NAME () {...}
export default COMPONENT_NAME;
*/
const { name } = node.declaration;
state.storiesFunctionDeclaration = state.potentialStoriesFunctionDeclaration?.find((potential) => potential.id.name === name);
}

@@ -97,6 +100,1 @@ },

}
/**
*:The main component function of those stories file _(`*.stories.svelte`)_ will always end up with `_stories`.
* @see {@link "file://./../../../utils/get-component-name.ts"}
*/
const isStoriesComponentFn = (fnDeclaration) => fnDeclaration.id?.name.endsWith('_stories');

@@ -1,2 +0,2 @@

import type { CallExpression } from 'estree';
import type { CallExpression, ExpressionStatement } from 'estree';
import type { CompiledASTNodes } from './nodes.js';

@@ -7,4 +7,7 @@ interface Params {

}
type Result = CallExpression[];
type Result = (CallExpression | ExpressionStatement)[];
/**
* WARN: The content of this function body differs in the production/development mode
*/
export declare function extractStoriesNodesFromExportDefaultFn(params: Params): Promise<Result>;
export {};

@@ -0,8 +1,32 @@

/**
* WARN: The content of this function body differs in the production/development mode
*/
export async function extractStoriesNodesFromExportDefaultFn(params) {
const { walk } = await import('zimmerframe');
const { nodes, filename } = params;
const { nodes } = params;
const { storiesFunctionDeclaration, storyIdentifier } = nodes;
const state = [];
const visitors = {
CallExpression(node, { state }) {
ExpressionStatement(node, { state, next }) {
if (process.env.NODE_ENV !== 'development') {
next();
}
// 🫠 ... some fun with AST
// Good to know:
// I can't split into smaller if-statements,
// because TypeScript wouldn't understand that `next()` gets out of the currently visited node scope - aka `return;`
if (node.expression.type === 'CallExpression' &&
node.expression.callee.type === 'CallExpression' &&
node.expression.callee.callee.type === 'MemberExpression' &&
node.expression.callee.callee.object.type === 'Identifier' &&
node.expression.callee.callee.object.name === '$' &&
node.expression.callee.arguments[0].type === 'Identifier' &&
node.expression.callee.arguments[0].name === storyIdentifier.name) {
state.push(node);
}
},
CallExpression(node, { state, next }) {
if (process.env.NODE_ENV !== 'production') {
next();
}
if (node.callee.type === 'Identifier' && node.callee.name === storyIdentifier.name) {

@@ -13,4 +37,4 @@ state.push(node);

};
walk(storiesFunctionDeclaration, state, visitors);
walk(storiesFunctionDeclaration.body, state, visitors);
return state;
}

@@ -8,3 +8,3 @@ import { describe, it } from 'vitest';

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -11,0 +11,0 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

/// <reference types="svelte" />
import type { Comment, Component, Fragment } from 'svelte/compiler';
import type { Comment, Component, Fragment, SnippetBlock } from 'svelte/compiler';
import type { extractModuleNodes } from './module-nodes.js';
import type { extractInstanceNodes } from './instance-nodes.js';
interface Result {
storyComponents: Array<{
/** Leading HTML comment as AST nodes which can be used as description for the story. */
comment?: Comment;
/** '<Story>' component AST node. */
component: Component;
}>;
/**
* "First level" _(at the root of fragment)_ snippet blocks AST nodes, which can be used for further transformation.
*
* For example:
* Determining the source code of the `<Story />`.
* Based on either `setTemplate` call,
* or by passing `children` as prop from the outer Svelte snippet block definition - e.g. `Story children={template1} />`.
*/
snippetBlocks: SnippetBlock[];
}

@@ -13,2 +25,3 @@ interface Params {

filename?: string;
instanceNodes: Awaited<ReturnType<typeof extractInstanceNodes>>;
moduleNodes: Awaited<ReturnType<typeof extractModuleNodes>>;

@@ -15,0 +28,0 @@ }

@@ -8,3 +8,3 @@ /**

const { walk } = await import('zimmerframe');
const { fragment, filename, moduleNodes } = params;
const { fragment, moduleNodes } = params;
const { storyIdentifier } = moduleNodes;

@@ -14,2 +14,3 @@ let latestComment;

storyComponents: [],
snippetBlocks: [],
};

@@ -30,2 +31,5 @@ const visitors = {

},
SnippetBlock(node, { state }) {
state.snippetBlocks.push(node);
},
};

@@ -32,0 +36,0 @@ walk(fragment, state, visitors);

import { describe, expect, it } from 'vitest';
import { extractFragmentNodes } from './fragment-nodes.js';
import { extractSvelteASTNodes } from './nodes.js';
import { extractInstanceNodes } from './instance-nodes.js';
import { extractModuleNodes } from './module-nodes.js';
import { getSvelteAST } from '../../../parser/ast.js';

@@ -8,3 +9,3 @@ describe(extractFragmentNodes.name, () => {

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -21,6 +22,11 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

});
const nodes = await extractSvelteASTNodes({ ast });
const moduleNodes = await extractModuleNodes({ module: ast.module });
const instanceNodes = await extractInstanceNodes({
instance: ast.instance,
moduleNodes,
});
const fragmentNodes = await extractFragmentNodes({
fragment: ast.fragment,
moduleNodes: nodes,
instanceNodes,
moduleNodes,
});

@@ -31,6 +37,7 @@ expect(fragmentNodes.storyComponents).toHaveLength(5);

}
expect(fragmentNodes.snippetBlocks).toHaveLength(0);
});
it("extracts '<Story />' leading HTML comments correctly", async () => {
const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -52,6 +59,11 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

});
const nodes = await extractSvelteASTNodes({ ast });
const moduleNodes = await extractModuleNodes({ module: ast.module });
const instanceNodes = await extractInstanceNodes({
instance: ast.instance,
moduleNodes,
});
const fragmentNodes = await extractFragmentNodes({
fragment: ast.fragment,
moduleNodes: nodes,
instanceNodes,
moduleNodes,
});

@@ -63,2 +75,50 @@ expect(fragmentNodes.storyComponents).toHaveLength(5);

});
it('extracts first level snippet blocks (at the root of fragment) correclty', async () => {
const ast = getSvelteAST({
code: `
<script context="module">
import { defineMeta, setTemplate } from "@storybook/addon-svelte-csf"
const { Story } = defineMeta();
</script>
<script>
setTemplate(render);
</script>
{#snippet render(args)}
<SampleComponent {...args} />
{/snippet}
{#snippet template1(args)}
<SampleComponent {...args} />
{/snippet}
{#snippet template2(args)}
<SampleComponent {...args} />
{/snippet}
<Story name="Example1" />
<Story name="Example2">
{#snippet children(args)}
<SampleComponent {...args} />
{/snippet}
</Story>
`,
});
const moduleNodes = await extractModuleNodes({ module: ast.module });
const instanceNodes = await extractInstanceNodes({
instance: ast.instance,
moduleNodes,
});
const fragmentNodes = await extractFragmentNodes({
fragment: ast.fragment,
instanceNodes,
moduleNodes,
});
expect(fragmentNodes.snippetBlocks).toHaveLength(3);
expect(fragmentNodes.snippetBlocks[0].expression.name).toBe('render');
expect(fragmentNodes.snippetBlocks[1].expression.name).toBe('template1');
expect(fragmentNodes.snippetBlocks[2].expression.name).toBe('template2');
});
});

@@ -7,6 +7,11 @@ /// <reference types="svelte" />

* Import specifier for `defineMeta` imported from this addon package.
* Could be renamed - e.g. `import { defineMeta } from "@storybook/addon-svelte-csf"`
* Could be renamed - e.g. `import { defineMeta as df } from "@storybook/addon-svelte-csf"`
*/
defineMetaImport: ImportSpecifier;
/**
* Import specifier for `setTemplate` imported from this addon package.
* Could be renamed - e.g. `import { setTemplate as st } from "@storybook/addon-svelte-csf"`
*/
setTemplateImport: ImportSpecifier | undefined;
/**
* Variable declaration: `const { Story } = defineMeta({ })`

@@ -13,0 +18,0 @@ * Could be destructured with rename - e.g. `const { Story: S } = defineMeta({ ... })`

import pkg from '@storybook/addon-svelte-csf/package.json' with { type: 'json' };
const AST_NODES_NAMES = {
defineMeta: 'defineMeta',
setTemplate: 'setTemplate',
Story: 'Story',

@@ -36,2 +37,5 @@ };

}
if (node.imported.name === AST_NODES_NAMES.setTemplate) {
state.setTemplateImport = node;
}
},

@@ -59,3 +63,3 @@ VariableDeclaration(node, { state }) {

walk(module.content, state, visitors);
const { defineMetaImport, defineMetaVariableDeclaration, storyIdentifier } = state;
const { defineMetaImport, setTemplateImport, defineMetaVariableDeclaration, storyIdentifier } = state;
if (!defineMetaImport) {

@@ -72,2 +76,3 @@ throw new Error(`Could not find '${AST_NODES_NAMES.defineMeta}' imported from the "${pkg.name}" in the stories file: ${filename}`);

defineMetaImport,
setTemplateImport,
defineMetaVariableDeclaration,

@@ -74,0 +79,0 @@ storyIdentifier,

@@ -7,3 +7,3 @@ import { describe, it } from 'vitest';

const { module } = getSvelteAST({
source: `<script></script>`,
code: `<script></script>`,
});

@@ -14,3 +14,3 @@ expect(extractModuleNodes({ module })).rejects.toThrow();

const { module } = getSvelteAST({
source: `<script context="module"></script>`,
code: `<script context="module"></script>`,
});

@@ -21,3 +21,3 @@ expect(extractModuleNodes({ module })).rejects.toThrow();

const { module } = getSvelteAST({
source: `
code: `
<script context="module">

@@ -32,3 +32,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf";

const { module } = getSvelteAST({
source: `
code: `
<script context="module">

@@ -43,3 +43,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const { module } = getSvelteAST({
source: `
code: `
<script context="module">

@@ -54,3 +54,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const { module } = getSvelteAST({
source: `
code: `
<script context="module">

@@ -64,7 +64,38 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

});
it("works when 'setTemplate' is used in stories", async ({ expect }) => {
const { module } = getSvelteAST({
code: `
<script context="module">
import { defineMeta, setTemplate } from "@storybook/addon-svelte-csf"
const { Story } = defineMeta();
</script>
`,
});
const nodes = await extractModuleNodes({ module });
expect(nodes.defineMetaImport).toBeDefined();
expect(nodes.setTemplateImport).toBeDefined();
expect(nodes.setTemplateImport?.local.name).toBe('setTemplate');
expect(nodes.defineMetaVariableDeclaration).toBeDefined();
expect(nodes.storyIdentifier).toBeDefined();
});
it("works when 'setTemplate' is NOT used in stories", async ({ expect }) => {
const { module } = getSvelteAST({
code: `
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf"
const { Story } = defineMeta();
</script>
`,
});
const nodes = await extractModuleNodes({ module });
expect(nodes.defineMetaImport).toBeDefined();
expect(nodes.setTemplateImport).toBeUndefined();
expect(nodes.defineMetaVariableDeclaration).toBeDefined();
expect(nodes.storyIdentifier).toBeDefined();
});
it('works on renamed identifiers', async ({ expect }) => {
const { module } = getSvelteAST({
source: `
code: `
<script context="module">
import { defineMeta as dm } from "@storybook/addon-svelte-csf"
import { defineMeta as dm, setTemplate as st } from "@storybook/addon-svelte-csf"
const { Story: S, meta: m } = dm();

@@ -76,4 +107,6 @@ </script>

expect(nodes.defineMetaImport.local.name).toBe('dm');
expect(nodes.setTemplateImport?.local.name).toBe('st');
expect(nodes.defineMetaVariableDeclaration).toBeDefined();
expect(nodes.storyIdentifier.name).toBe('S');
});
});

@@ -5,2 +5,3 @@ /// <reference types="svelte" />

import { extractFragmentNodes } from './fragment-nodes.js';
import { extractInstanceNodes } from './instance-nodes.js';
/**

@@ -10,3 +11,3 @@ * Selected nodes extracted from the Svelte AST via `svelte.compile`,

*/
export type SvelteASTNodes = Awaited<ReturnType<typeof extractModuleNodes>> & Awaited<ReturnType<typeof extractFragmentNodes>>;
export type SvelteASTNodes = Awaited<ReturnType<typeof extractModuleNodes>> & Awaited<ReturnType<typeof extractFragmentNodes>> & Awaited<ReturnType<typeof extractInstanceNodes>>;
interface Params {

@@ -13,0 +14,0 @@ ast: Root;

import { extractModuleNodes } from './module-nodes.js';
import { extractFragmentNodes } from './fragment-nodes.js';
import { extractInstanceNodes } from './instance-nodes.js';
/**

@@ -8,4 +9,9 @@ * Pick only required Svelte AST nodes for further usage in this addon.

const { ast, filename } = params;
const { module, fragment } = ast;
const { module, fragment, instance } = ast;
const moduleNodes = await extractModuleNodes({ module, filename });
const instanceNodes = await extractInstanceNodes({
instance,
filename,
moduleNodes,
});
const fragmentNodes = await extractFragmentNodes({

@@ -15,7 +21,9 @@ fragment,

moduleNodes,
instanceNodes,
});
return {
...moduleNodes,
...instanceNodes,
...fragmentNodes,
};
}

@@ -7,3 +7,3 @@ import { describe, it } from 'vitest';

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -10,0 +10,0 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

@@ -1,7 +0,6 @@

/// <reference types="svelte" />
import type { StoryObj } from '@storybook/svelte';
import type { Meta } from '@storybook/svelte';
import type { ComponentProps } from 'svelte';
import type { Attribute, Component } from 'svelte/compiler';
type StoryAttributes = Array<keyof (StoryObj & {
exportName: string;
})>;
import type Story from '../../../../runtime/Story.svelte';
type StoryAttributes = Array<keyof ComponentProps<Story<Meta>>>;
interface Options<Attributes extends StoryAttributes> {

@@ -8,0 +7,0 @@ component: Component;

@@ -8,3 +8,3 @@ import { describe, expect, it } from 'vitest';

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -30,3 +30,3 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

const ast = getSvelteAST({
source: `
code: `
<script context="module">

@@ -33,0 +33,0 @@ import { defineMeta } from "@storybook/addon-svelte-csf"

@@ -16,7 +16,8 @@ import type { StoryObj } from '@storybook/svelte';

*/
export declare const createRuntimeStories: <TMeta extends import("@storybook/types").ComponentAnnotations<import("@storybook/svelte").SvelteRenderer<import("svelte").SvelteComponent<Record<string, any>, any, any>>, import("@storybook/types").Args>>(Stories: ComponentType, meta: TMeta) => Record<string, import("@storybook/types").StoryAnnotations<import("@storybook/svelte").SvelteRenderer<StoryRenderer<TMeta>>, {
export declare const createRuntimeStories: <TMeta extends import("@storybook/types").ComponentAnnotations<import("@storybook/svelte").SvelteRenderer<import("svelte").SvelteComponent<Record<string, any>, any, any>>, import("@storybook/types").Args>>(Stories: ComponentType, meta: TMeta, codeByStoryMap: Record<string, string>) => Record<string, import("@storybook/types").StoryAnnotations<import("@storybook/svelte").SvelteRenderer<StoryRenderer<TMeta>>, {
Stories: ComponentType;
exportName: string;
code: string;
args: StoryObj<TMeta>["args"];
storyContext: import("@storybook/svelte").StoryContext<TMeta["args"]>;
}>>;

@@ -22,3 +22,3 @@ /* eslint-env browser */

// TODO: I'm not sure the 'meta' is necessary here. As long as it's default exported, SB should internally combine it with the stories. Except for the play logic below, that looks funky, need to ask Pablo about that.
export const createRuntimeStories = (Stories, meta) => {
export const createRuntimeStories = (Stories, meta, codeByStoryMap) => {
const repository = {

@@ -49,2 +49,3 @@ stories: new Map(),

Stories,
code: codeByStoryMap[exportName],
storyContext,

@@ -55,30 +56,2 @@ args,

};
// TODO: Restore this feature
// if (storyMeta.rawSource) {
// storyFn.parameters = combineParameters(storyFn.parameters, {
// storySource: {
// source: storyMeta.rawSource,
// },
// });
// }
//
// if (storyMeta.source) {
// let code: string | undefined;
//
// if (storyMeta.source === true && storyMeta.rawSource) {
// code = storyMeta.rawSource;
// }
//
// if (typeof storyMeta.source === "string") {
// code = storyMeta.source;
// }
//
// if (code) {
// storyFn.parameters = combineParameters(storyFn.parameters, {
// docs: {
// source: { code },
// },
// });
// }
// }
const play = meta.play ?? story.play;

@@ -85,0 +58,0 @@ if (play) {

@@ -35,2 +35,4 @@ import { SvelteComponent } from "svelte";

/**
* @deprecated
* Is recommended to use `parameters={{ docs: { source: { code: "..." } } }}` instead, to follow Regular CSF.
* WARNING: This is under consideration to be revamped.

@@ -42,2 +44,7 @@ * Ref: https://github.com/storybookjs/addon-svelte-csf/pull/181#issuecomment-2130744977

* By default, sources for an args story are auto-generated.
*
* TODO:
* I am still confused on what is exactly supposed to happen/output when it's a boolean?
* For now this is not implemented, and I've created a warning when it happens.
*
* If source is true, then the source of the story will be used instead.

@@ -44,0 +51,0 @@ * If source is a string, it replaces the source of the story.

@@ -8,2 +8,3 @@ import { SvelteComponent } from "svelte";

exportName: string;
code: string;
args: StoryObj<TMeta>["args"];

@@ -10,0 +11,0 @@ storyContext: StoryContext<TMeta["args"]>;

@@ -1,1 +0,1 @@

export declare function getNameFromFilename(filename: string): string | null;
export declare function getComponentName(filename: string): string;

@@ -1,28 +0,11 @@

// From https://github.com/sveltejs/svelte/blob/8db3e8d0297e052556f0b6dde310ef6e197b8d18/src/compiler/compile/utils/get_name_from_filename.ts
// Copied because it is not exported from the compiler
export function getNameFromFilename(filename) {
if (!filename)
return null;
const parts = filename.split(/[/\\]/).map(encodeURI);
if (parts.length > 1) {
const indexMatch = parts[parts.length - 1].match(/^index(\.\w+)/);
if (indexMatch) {
parts.pop();
parts[parts.length - 1] += indexMatch[1];
}
// Copied from https://github.com/sveltejs/svelte/blob/14ddb87c311ff3280dde0ae44b0a0d864ec26353/packages/svelte/src/compiler/phases/2-analyze/index.js#L57-L69
export function getComponentName(filename) {
const parts = filename.split(/[/\\]/);
const basename = parts.pop();
const last_dir = parts.at(-1);
let name = basename.replace('.svelte', '');
if (name === 'index' && last_dir && last_dir !== 'src') {
name = last_dir;
}
if (!parts)
return null;
const base = parts
.pop()
?.replace(/%/g, 'u')
.replace(/\.[^.]+$/, '')
.replace(/[^a-zA-Z_$0-9]+/g, '_')
.replace(/^_/, '')
.replace(/_$/, '')
.replace(/^(\d)/, '_$1');
if (!base) {
throw new Error(`Could not derive component name from file ${filename}`);
}
return base[0].toUpperCase() + base.slice(1);
return name[0].toUpperCase() + name.slice(1);
}
{
"name": "@storybook/addon-svelte-csf",
"version": "5.0.0--canary.181.ef3ca0a.0",
"version": "5.0.0--canary.181.efeeaf8.0",
"description": "Allows to write stories in Svelte syntax",

@@ -56,2 +56,3 @@ "keywords": [

"magic-string": "^0.30.1",
"prettier-plugin-svelte": "^3.2.4",
"zimmerframe": "^1.1.2"

@@ -61,14 +62,15 @@ },

"@auto-it/released": "^10.32.6",
"@storybook/addon-actions": "^8.1.0",
"@storybook/addon-essentials": "^8.1.0",
"@storybook/addon-interactions": "^8.1.0",
"@storybook/client-logger": "^8.1.0",
"@storybook/csf": "^0.1.5",
"@storybook/addon-actions": "^8.1.6",
"@storybook/addon-essentials": "^8.1.6",
"@storybook/addon-interactions": "^8.1.6",
"@storybook/client-logger": "^8.1.6",
"@storybook/csf": "^0.1.8",
"@storybook/docs-tools": "^8.1.6",
"@storybook/eslint-config-storybook": "^3.1.2",
"@storybook/node-logger": "^8.1.0",
"@storybook/preview-api": "^8.1.0",
"@storybook/svelte": "^8.1.0",
"@storybook/svelte-vite": "^8.1.0",
"@storybook/test": "^8.1.0",
"@storybook/types": "^8.1.0",
"@storybook/node-logger": "^8.1.6",
"@storybook/preview-api": "^8.1.6",
"@storybook/svelte": "^8.1.6",
"@storybook/svelte-vite": "^8.1.6",
"@storybook/test": "^8.1.6",
"@storybook/types": "^8.1.6",
"@sveltejs/package": "^2.2.0",

@@ -78,3 +80,3 @@ "@sveltejs/vite-plugin-svelte": "4.0.0-next.1",

"@types/estree": "^1.0.5",
"@types/node": "^20.12.12",
"@types/node": "^20.14.2",
"@vitest/ui": "^1.6.0",

@@ -86,7 +88,7 @@ "auto": "^11.1.1",

"eslint-plugin-storybook": "^0.6.12",
"jsdom": "^24.0.0",
"prettier": "^3.0.0",
"jsdom": "^24.1.0",
"prettier": "^3.3.1",
"rimraf": "^5.0.7",
"storybook": "^8.1.0",
"rollup": "^4.18.0",
"storybook": "^8.1.6",
"svelte": "5.0.0-next.136",

@@ -93,0 +95,0 @@ "svelte-check": "^3.5.0",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc