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.363788a.0 to 5.0.0--canary.181.3f35763.0

dist/compiler/transform/appendix/create-export-default.test.d.ts

60

dist/compiler/plugin.js

@@ -13,11 +13,6 @@ /**

import { preprocess } from 'svelte/compiler';
import { createAppendix } from './transform/create-appendix.js';
import { removeExportDefault } from './transform/remove-export-default.js';
import { insertDefineMetaJSDocCommentAsDescription } from './transform/define-meta/description.js';
import { destructureMetaFromDefineMeta } from './transform/define-meta/destructure-meta.js';
import { updateCompiledStoryProps } from './transform/compiled-story-props.js';
import { getSvelteAST } from '../parser/ast.js';
import { extractStoriesNodesFromExportDefaultFn } from '../parser/extract/compiled/stories.js';
import { extractCompiledASTNodes } from '../parser/extract/compiled/nodes.js';
import { extractSvelteASTNodes } from '../parser/extract/svelte/nodes.js';
import { transformStoriesCode } from './transform';
import { getSvelteAST } from '#parser/ast';
import { extractCompiledASTNodes } from '#parser/extract/compiled/nodes';
import { extractSvelteASTNodes } from '#parser/extract/svelte/nodes';
export async function plugin() {

@@ -39,3 +34,3 @@ const [{ createFilter }, { loadSvelteConfig }] = await Promise.all([

let magicCompiledCode = new MagicString(compiledCode);
// @ts-expect-error FIXME: `this.originalCode` exists at runtime.
// @ts-expect-error FIXME: `this.originalCode` exists at runtime in the development mode only.
// Need to research if its documented somewhere

@@ -54,52 +49,15 @@ let rawCode = this.originalCode ?? fs.readFileSync(id).toString();

});
const compiledNodes = await extractCompiledASTNodes({
const compiledASTNodes = await extractCompiledASTNodes({
ast: compiledAST,
filename: id,
});
const extractedCompiledStoriesNodes = await extractStoriesNodesFromExportDefaultFn({
nodes: compiledNodes,
filename: id,
});
/*
* 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();
await Promise.all(compiledStories.map((compiled, index) => updateCompiledStoryProps({
await transformStoriesCode({
code: magicCompiledCode,
componentASTNodes: { svelte: svelteStories[index], compiled },
svelteASTNodes,
filename: id,
originalCode: rawCode,
})));
await destructureMetaFromDefineMeta({
code: magicCompiledCode,
nodes: compiledNodes,
filename: id,
});
insertDefineMetaJSDocCommentAsDescription({
code: magicCompiledCode,
nodes: {
compiled: compiledNodes,
svelte: svelteASTNodes,
compiled: compiledASTNodes,
},
filename: id,
originalCode: rawCode,
});
removeExportDefault({
code: magicCompiledCode,
nodes: compiledNodes,
filename: id,
});
await createAppendix({
code: magicCompiledCode,
nodes: {
compiled: compiledNodes,
svelte: svelteASTNodes,
},
filename: id,
});
return {

@@ -106,0 +64,0 @@ code: magicCompiledCode.toString(),

import type { ExportDefaultDeclaration } from 'estree';
import type { getMetaIdentifier } from '../../../parser/analyse/meta/identifier.js';
import type { getMetaIdentifier } from '#parser/analyse/define-meta/meta-identifier';
interface Params {
metaIdentifier: ReturnType<typeof getMetaIdentifier>;
filename: string;
filename?: string;
}
export declare function createExportDefaultMeta(params: Params): ExportDefaultDeclaration;
export {};
export function createExportDefaultMeta(params) {
const { metaIdentifier, filename } = params;
const { metaIdentifier } = params;
return {

@@ -4,0 +4,0 @@ type: 'ExportDefaultDeclaration',

import type { ExportNamedDeclaration } from 'estree';
import type { getStoriesIdentifiers } from '../../../parser/analyse/Story/attributes/identifiers.js';
import type { getStoriesIdentifiers } from '#parser/analyse/story/attributes/identifiers';
interface Params {
storyIdentifiers: ReturnType<typeof getStoriesIdentifiers>;
filename: string;
filename?: string;
}
export declare function createExportOrderVariable(params: Params): ExportNamedDeclaration;
export {};

@@ -1,4 +0,7 @@

const EXPORT_ORDER_VARIABLE = '__namedExportsOrder';
export function createExportOrderVariable(params) {
const { storyIdentifiers } = params;
const exported = {
type: 'Identifier',
name: '__namedExportsOrder',
};
return {

@@ -9,10 +12,4 @@ type: 'ExportNamedDeclaration',

type: 'ExportSpecifier',
local: {
type: 'Identifier',
name: EXPORT_ORDER_VARIABLE,
},
exported: {
type: 'Identifier',
name: EXPORT_ORDER_VARIABLE,
},
local: exported,
exported,
},

@@ -26,6 +23,3 @@ ],

type: 'VariableDeclarator',
id: {
type: 'Identifier',
name: EXPORT_ORDER_VARIABLE,
},
id: exported,
init: {

@@ -32,0 +26,0 @@ type: 'ArrayExpression',

import type { ImportDeclaration } from 'estree';
/**
* the export is defined in the `package.json` export map
* The export is defined in the `package.json` export map
*/
export declare function createImport(): ImportDeclaration;
export declare function createRuntimeStoriesImport(): ImportDeclaration;
import pkg from '@storybook/addon-svelte-csf/package.json' with { type: 'json' };
/**
* the export is defined in the `package.json` export map
* The export is defined in the `package.json` export map
*/
export function createImport() {
export function createRuntimeStoriesImport() {
const imported = {

@@ -7,0 +7,0 @@ type: 'Identifier',

@@ -5,6 +5,6 @@ import type { ExportNamedDeclaration } from 'estree';

exportName: string;
filename: string;
filename?: string;
node: ReturnType<typeof createVariableFromRuntimeStoriesCall>;
}
export declare function createNamedExportStory(params: Params): Promise<ExportNamedDeclaration>;
export declare function createNamedExportStory(params: Params): ExportNamedDeclaration;
export {};

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

export async function createNamedExportStory(params) {
export function createNamedExportStory(params) {
const { exportName, node } = params;

@@ -3,0 +3,0 @@ const exported = {

import type { FunctionDeclaration, VariableDeclaration } from 'estree';
import type { getMetaIdentifier } from '../../../parser/analyse/meta/identifier.js';
import type { getMetaIdentifier } from '#parser/analyse/define-meta/meta-identifier';
interface Params {
storiesFunctionDeclaration: FunctionDeclaration;
metaIdentifier: ReturnType<typeof getMetaIdentifier>;
codeByStoryMapDeclaration: VariableDeclaration;
filename: string;
filename?: string;
}
export declare function createVariableFromRuntimeStoriesCall(params: Params): VariableDeclaration;
export {};
export function createVariableFromRuntimeStoriesCall(params) {
const { storiesFunctionDeclaration, metaIdentifier, codeByStoryMapDeclaration } = params;
const { storiesFunctionDeclaration, metaIdentifier } = params;
return {

@@ -15,2 +15,3 @@ type: 'VariableDeclaration',

type: 'CallExpression',
optional: false,
callee: {

@@ -28,8 +29,3 @@ type: 'Identifier',

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

@@ -36,0 +32,0 @@ },

import MagicString from 'magic-string';
import type { CompiledASTNodes } from '../../parser/extract/compiled/nodes.js';
import type { SvelteASTNodes } from '../../parser/extract/svelte/nodes.js';
import type { CompiledASTNodes } from '#parser/extract/compiled/nodes';
import type { SvelteASTNodes } from '#parser/extract/svelte/nodes';
interface Params {

@@ -10,5 +10,5 @@ code: MagicString;

};
filename: string;
filename?: string;
}
export declare function createAppendix(params: Params): Promise<void>;
export {};
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';
import { createImport } from './appendix/create-import.js';
import { createVariableFromRuntimeStoriesCall } from './appendix/create-variable-from-runtime-stories-call.js';
import { createNamedExportStory } from './appendix/create-named-export-story.js';
import { getMetaIdentifier } from '../../parser/analyse/meta/identifier.js';
import { getStoriesIdentifiers } from '../../parser/analyse/Story/attributes/identifiers.js';
import { createExportDefaultMeta } from './appendix/create-export-default';
import { createExportOrderVariable } from './appendix/create-export-order';
import { createRuntimeStoriesImport } from './appendix/create-import';
import { createVariableFromRuntimeStoriesCall } from './appendix/create-variable-from-runtime-stories-call';
import { createNamedExportStory } from './appendix/create-named-export-story';
import { getMetaIdentifier } from '#parser/analyse/define-meta/meta-identifier';
import { getStoriesIdentifiers } from '#parser/analyse/story/attributes/identifiers';
export async function createAppendix(params) {

@@ -15,3 +14,6 @@ const { code, nodes, filename } = params;

const { defineMetaVariableDeclaration, storiesFunctionDeclaration } = compiled;
const storyIdentifiers = await getStoriesIdentifiers({ nodes: svelte, filename });
const storyIdentifiers = getStoriesIdentifiers({
nodes: svelte,
filename,
});
const metaIdentifier = getMetaIdentifier({

@@ -21,14 +23,12 @@ node: defineMetaVariableDeclaration,

});
const codeByStoryMapDeclaration = createCodeByStoryMap({ storyIdentifiers });
const variableFromRuntimeStoriesCall = createVariableFromRuntimeStoriesCall({
storiesFunctionDeclaration,
metaIdentifier,
codeByStoryMapDeclaration,
filename,
});
const storiesExports = await Promise.all(storyIdentifiers.map(({ exportName }) => createNamedExportStory({
const storiesExports = storyIdentifiers.map(({ exportName }) => createNamedExportStory({
exportName,
filename,
node: variableFromRuntimeStoriesCall,
})));
}));
const appendix = toJs({

@@ -38,4 +38,3 @@ type: 'Program',

body: [
createImport(),
createCodeByStoryMap({ storyIdentifiers }),
createRuntimeStoriesImport(),
variableFromRuntimeStoriesCall,

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

@@ -1,9 +0,23 @@

import type MagicString from 'magic-string';
import type { CompiledASTNodes } from '../../../parser/extract/compiled/nodes.js';
import type { CompiledASTNodes } from '#parser/extract/compiled/nodes';
interface Params {
code: MagicString;
nodes: CompiledASTNodes;
filename: string;
filename?: string;
}
export declare function destructureMetaFromDefineMeta(params: Params): Promise<void>;
/**
* Attempt to destructure 'meta' identifier in the object pattern of the variable declaration from call `defineMeta({...})`
* if it wasn't done by user manually.
*
* Before:
*
* ```js
* const { Story } = defineMeta({});
* ```
*
* After:
*
* ```js
* const { Story, meta } = defineMeta({});
* ```
*/
export declare function destructureMetaFromDefineMeta(params: Params): void;
export {};

@@ -1,4 +0,20 @@

import { toJs } from 'estree-util-to-js';
export async function destructureMetaFromDefineMeta(params) {
const { code, nodes, filename } = params;
import { NoDestructuredDefineMetaCallError } from '#utils/error/parser/analyse/define-meta';
/**
* Attempt to destructure 'meta' identifier in the object pattern of the variable declaration from call `defineMeta({...})`
* if it wasn't done by user manually.
*
* Before:
*
* ```js
* const { Story } = defineMeta({});
* ```
*
* After:
*
* ```js
* const { Story, meta } = defineMeta({});
* ```
*/
export function destructureMetaFromDefineMeta(params) {
const { nodes, filename } = params;
const { defineMetaVariableDeclaration } = nodes;

@@ -8,13 +24,14 @@ const { declarations } = defineMetaVariableDeclaration;

if (id.type !== 'ObjectPattern') {
// TODO: make error message more user friendly
// what happened, how to fix
throw new Error(`Internal error during attempt to destructure 'meta' from 'defineMeta({ ... })' - expected object pattern. Stories file: ${filename}`);
throw new NoDestructuredDefineMetaCallError({
filename,
defineMetaVariableDeclarator: declarations[0],
});
}
const hasDestructuredMeta = id.properties.find((p) => {
const destructuredMeta = id.properties.find((p) => {
return p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === 'meta';
});
if (hasDestructuredMeta) {
if (destructuredMeta) {
return;
}
const metaProperty = {
id.properties.push({
type: 'Property',

@@ -33,11 +50,3 @@ kind: 'init',

method: false,
};
id.properties.push(metaProperty);
// @ts-expect-error FIXME: No idea which type includes start/end, they exist at runtime
const { start, end } = defineMetaVariableDeclaration;
code.update(start, end, toJs({
type: 'Program',
sourceType: 'module',
body: [defineMetaVariableDeclaration],
}).value);
});
}
import type MagicString from 'magic-string';
import type { CompiledASTNodes } from '../../parser/extract/compiled/nodes.js';
import type { CompiledASTNodes } from '#parser/extract/compiled/nodes';
interface Params {
code: MagicString;
nodes: CompiledASTNodes;
filename: string;
filename?: string;
}

@@ -8,0 +8,0 @@ /**

@@ -7,3 +7,3 @@ import { toJs } from 'estree-util-to-js';

export function removeExportDefault(params) {
const { code, nodes, filename } = params;
const { code, nodes } = params;
const { exportDefault, storiesFunctionDeclaration } = nodes;

@@ -13,2 +13,3 @@ if (exportDefault.declaration.type === 'FunctionDeclaration') {

const { start, end } = exportDefault;
// NOTE: It updates code by removing `export default` from the stories function declaration.
code.update(start, end, toJs({

@@ -15,0 +16,0 @@ type: 'Program',

@@ -5,3 +5,3 @@ import type { Meta, StoryContext as BaseStoryContext, StoryObj } from '@storybook/svelte';

export type { Story };
export { setTemplate } from './runtime/contexts/template.svelte.js';
export { setTemplate } from './runtime/contexts/template.svelte';
export declare function defineMeta<const TMeta extends Meta = Meta>(meta: TMeta): {

@@ -8,0 +8,0 @@ Story: typeof Story<TMeta>;

import Story from './runtime/Story.svelte';
export { setTemplate } from './runtime/contexts/template.svelte.js';
export { setTemplate } from './runtime/contexts/template.svelte';
export function defineMeta(meta) {

@@ -4,0 +4,0 @@ return {

import fs from 'node:fs/promises';
import { combineTags } from '@storybook/csf';
import { preprocess } from 'svelte/compiler';
import { getSvelteAST } from '../parser/ast.js';
import { extractSvelteASTNodes } from '../parser/extract/svelte/nodes.js';
import { extractMetaPropertiesNodes } from '../parser/extract/meta-properties.js';
import { extractStoryAttributesNodes } from '../parser/extract/svelte/Story/attributes.js';
import { getMetaIdValue, getMetaTagsValue, getMetaTitleValue, } from '../parser/analyse/meta/properties.js';
import { getTagsFromStoryAttribute } from '../parser/analyse/Story/attributes/tags.js';
import { getStoryIdentifiers } from '../parser/analyse/Story/attributes/identifiers.js';
import { getSvelteAST } from '#parser/ast';
import { extractSvelteASTNodes } from '#parser/extract/svelte/nodes';
import { extractDefineMetaPropertiesNodes } from '#parser/extract/svelte/define-meta';
import { extractStoryAttributesNodes } from '#parser/extract/svelte/story/attributes';
import { getPropertyArrayOfStringsValue, getPropertyStringValue, } from '#parser/analyse/define-meta/properties';
import { getArrayOfStringsValueFromAttribute } from '#parser/analyse/story/attributes';
import { getStoryIdentifiers } from '#parser/analyse/story/attributes/identifiers';
export const indexer = {

@@ -25,24 +25,35 @@ test: /\.svelte$/,

const svelteAST = getSvelteAST({ code, filename });
const nodes = await extractSvelteASTNodes({ ast: svelteAST, filename });
const metaPropertiesNodes = extractMetaPropertiesNodes({
nodes,
const svelteASTNodes = await extractSvelteASTNodes({
ast: svelteAST,
filename,
});
const metaPropertiesNodes = extractDefineMetaPropertiesNodes({
nodes: svelteASTNodes,
filename,
properties: ['id', 'title', 'tags'],
});
const storiesAttributesNodes = nodes.storyComponents.map(({ component }) => extractStoryAttributesNodes({
component,
filename,
attributes: ['exportName', 'name', 'tags'],
}));
const metaTitle = metaPropertiesNodes.title
? makeTitle(getMetaTitleValue({ node: metaPropertiesNodes.title, filename }))
? makeTitle(getPropertyStringValue({
node: metaPropertiesNodes.title,
filename,
}))
: undefined;
const metaTags = metaPropertiesNodes.tags
? getMetaTagsValue({ node: metaPropertiesNodes.tags, filename })
? getPropertyArrayOfStringsValue({
node: metaPropertiesNodes.tags,
filename,
})
: [];
// TODO: Verify if we can remove it
const metaId = metaPropertiesNodes.id
? getMetaIdValue({ node: metaPropertiesNodes.id, filename })
? getPropertyStringValue({ node: metaPropertiesNodes.id, filename })
: undefined;
return storiesAttributesNodes.map((attributeNode) => {
return svelteASTNodes.storyComponents.map(({ component }) => {
const attributeNode = extractStoryAttributesNodes({
component,
filename,
attributes: ['exportName', 'name', 'tags'],
});
const { exportName, name } = getStoryIdentifiers({
component,
nameNode: attributeNode.name,

@@ -52,2 +63,7 @@ exportNameNode: attributeNode.exportName,

});
const tags = getArrayOfStringsValueFromAttribute({
node: attributeNode.tags,
filename,
component,
});
return {

@@ -59,3 +75,3 @@ type: 'story',

title: metaTitle,
tags: combineTags(...metaTags, ...getTagsFromStoryAttribute({ node: attributeNode.tags, filename })),
tags: combineTags(...metaTags, ...tags),
};

@@ -62,0 +78,0 @@ });

import type { ExportDefaultDeclaration, FunctionDeclaration, Identifier, ImportSpecifier, Program, VariableDeclaration } from 'estree';
/**
* Important AST nodes from the compiled output of a single `*.stories.svelte` file.
* They are needed for further code transformation by this addon.
* Powered by `rollup`'s internal [`this.parse()`](https://rollupjs.org/plugin-development/#this-parse)
*/
export interface CompiledASTNodes {

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

import pkg from '@storybook/addon-svelte-csf/package.json' with { type: 'json' };
import { MissingDefineMetaVariableDeclarationError, MissingImportedDefineMetaError, NoExportDefaultError, NoStoriesFunctionDeclarationError, NoStoryIdentifierFoundError, } from '#utils/error/parser/extract/compiled';
import { DefaultOrNamespaceImportUsedError } from '#utils/error/parser/extract/svelte';
const AST_NODES_NAMES = {

@@ -11,4 +13,4 @@ defineMeta: 'defineMeta',

export async function extractCompiledASTNodes(params) {
const { ast, filename } = params;
const { walk } = await import('zimmerframe');
const { ast, filename } = params;
const state = { potentialStoriesFunctionDeclaration: [] };

@@ -21,3 +23,3 @@ const visitors = {

if (specifier.type !== 'ImportSpecifier') {
throw new Error(`Don't use the default/namespace import from "${pkg.name}" in the stories file: ${filename}`);
throw new DefaultOrNamespaceImportUsedError(filename);
}

@@ -78,15 +80,15 @@ visit(specifier, state);

if (!defineMetaImport) {
throw new Error(`Could not find '${AST_NODES_NAMES.defineMeta}' imported from the "${pkg.name}" in the compiled output of stories file: ${filename}`);
throw new MissingImportedDefineMetaError(filename);
}
if (!defineMetaVariableDeclaration) {
throw new Error(`Could not find '${defineMetaImport.local.name}({ ... })' in the compiled output of the stories file: ${filename}`);
throw new MissingDefineMetaVariableDeclarationError(filename);
}
if (!exportDefault) {
throw new Error(`Could not find 'export default' in the compiled output of the stories file: ${filename}`);
throw new NoExportDefaultError(filename);
}
if (!storyIdentifier) {
throw new Error(`Could not find 'Story' identifier in the compiled output of the stories file: ${filename}`);
throw new NoStoryIdentifierFoundError(filename);
}
if (!storiesFunctionDeclaration) {
throw new Error(`Could not find the stories component '*.stories.svelte' function in the compiled output of the stories file: ${filename}`);
throw new NoStoriesFunctionDeclarationError(filename);
}

@@ -93,0 +95,0 @@ return {

import type { CallExpression, ExpressionStatement } from 'estree';
import type { CompiledASTNodes } from './nodes.js';
import type { CompiledASTNodes } from './nodes';
interface Params {
nodes: CompiledASTNodes;
filename: string;
filename?: string;
}

@@ -7,0 +7,0 @@ type Result = (CallExpression | ExpressionStatement)[];

/// <reference types="svelte" />
import type { Comment, Component, Fragment, SnippetBlock } from 'svelte/compiler';
import type { extractModuleNodes } from './module-nodes.js';
import type { extractInstanceNodes } from './instance-nodes.js';
import type { extractModuleNodes } from './module-nodes';
import type { extractInstanceNodes } from './instance-nodes';
interface Result {

@@ -6,0 +6,0 @@ storyComponents: Array<{

import { describe, expect, it } from 'vitest';
import { extractFragmentNodes } from './fragment-nodes.js';
import { extractInstanceNodes } from './instance-nodes.js';
import { extractModuleNodes } from './module-nodes.js';
import { getSvelteAST } from '../../../parser/ast.js';
import { extractFragmentNodes } from './fragment-nodes';
import { extractInstanceNodes } from './instance-nodes';
import { extractModuleNodes } from './module-nodes';
import { getSvelteAST } from '#parser/ast';
describe(extractFragmentNodes.name, () => {

@@ -7,0 +7,0 @@ it("extracts '<Story />' AST nodes correctly", async () => {

/// <reference types="svelte" />
import type { CallExpression } from 'estree';
import type { Root } from 'svelte/compiler';
import type { extractModuleNodes } from './module-nodes.js';
import type { extractModuleNodes } from './module-nodes';
interface Result {

@@ -6,0 +6,0 @@ setTemplateCall: CallExpression | undefined;

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

export async function extractInstanceNodes(options) {
const { instance, moduleNodes, filename } = options;
const { instance, moduleNodes } = options;
const { setTemplateImport } = moduleNodes;

@@ -11,0 +11,0 @@ if (!instance || !setTemplateImport) {

import { describe, expect, it } from 'vitest';
import { getSvelteAST } from '../../../parser/ast.js';
import { extractInstanceNodes } from './instance-nodes.js';
import { extractModuleNodes } from './module-nodes.js';
import { extractInstanceNodes } from './instance-nodes';
import { extractModuleNodes } from './module-nodes';
import { getSvelteAST } from '#parser/ast';
describe(extractInstanceNodes.name, () => {

@@ -6,0 +6,0 @@ it("extract 'setTemplateCall' correctly when used", async () => {

import pkg from '@storybook/addon-svelte-csf/package.json' with { type: 'json' };
import { DefaultOrNamespaceImportUsedError, MissingDefineMetaImportError, MissingDefineMetaVariableDeclarationError, MissingModuleTagError, NoStoryComponentDestructuredError, } from '#utils/error/parser/extract/svelte';
const AST_NODES_NAMES = {

@@ -14,6 +15,4 @@ defineMeta: 'defineMeta',

const { module, filename } = options;
// TODO: Perhaps we can use some better way to insert error messages?
// String interpolations doesn't feel right if we want to provide a whole example (code snippet).
if (!module) {
throw new Error(`Couldn't find a module tag. Add (<script context="module">) to the stories file: ${filename}`);
throw new MissingModuleTagError(filename);
}

@@ -28,3 +27,3 @@ const { walk } = await import('zimmerframe');

if (specifier.type !== 'ImportSpecifier') {
throw new Error(`Don't use the default/namespace import from "${pkg.name}" in the stories file: ${filename}`);
throw new DefaultOrNamespaceImportUsedError(filename);
}

@@ -66,9 +65,9 @@ visit(specifier, state);

if (!defineMetaImport) {
throw new Error(`Could not find '${AST_NODES_NAMES.defineMeta}' imported from the "${pkg.name}" in the stories file: ${filename}`);
throw new MissingDefineMetaImportError(filename);
}
if (!defineMetaVariableDeclaration) {
throw new Error(`Could not find '${defineMetaImport.local.name}({ ... })' call in the module tag ('<script context="module">') of the stories file: ${filename}`);
throw new MissingDefineMetaVariableDeclarationError(filename);
}
if (!storyIdentifier) {
throw new Error(`Component 'Story' was not destructured from the '${defineMetaImport.local.name}({ ... })' function call. Use 'const { Story } = defineMeta({ ... })' in the stories file: ${filename}`);
throw new NoStoryComponentDestructuredError({ filename, defineMetaImport });
}

@@ -75,0 +74,0 @@ return {

import { describe, it } from 'vitest';
import { extractModuleNodes } from './module-nodes.js';
import { getSvelteAST } from '../../../parser/ast.js';
import { extractModuleNodes } from './module-nodes';
import { getSvelteAST } from '#parser/ast';
describe(extractModuleNodes.name, () => {

@@ -9,3 +9,14 @@ it('fails when module tag not found', ({ expect }) => {

});
expect(extractModuleNodes({ module })).rejects.toThrow();
expect(extractModuleNodes({ module })).rejects.toThrowErrorMatchingInlineSnapshot(`
[SB_SVELTE_CSF_PARSER_EXTRACT_SVELTE_0001 (MissingModuleTagError): Stories file: <path not specified>
doesn't have a module tag _(\`<script context="module"> <!-- ... --> </script>\`)_.
Make sure this stories file has initial code snippet in order for this addon to work correctly:
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
const { Story } = defineMeta({});
</script>]
`);
});

@@ -16,3 +27,15 @@ it("fails when 'defineMeta' not imported", ({ expect }) => {

});
expect(extractModuleNodes({ module })).rejects.toThrow();
expect(extractModuleNodes({ module })).rejects.toThrowErrorMatchingInlineSnapshot(`
[SB_SVELTE_CSF_PARSER_EXTRACT_SVELTE_0003 (MissingDefineMetaImportError): Stories file: <path not specified>
doesn't have a 'defineMeta' imported from the "@storybook/addon-svelte-csf" package inside the module tag.
('<script context="module"> <!-- ... --> </script>').
Make sure this stories file has initial code snippet in order for this addon to work correctly:
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
const { Story } = defineMeta({});
</script>]
`);
});

@@ -27,3 +50,14 @@ it("fails when 'defineMeta' not used", ({ expect }) => {

});
expect(extractModuleNodes({ module })).rejects.toThrow();
expect(extractModuleNodes({ module })).rejects.toThrowErrorMatchingInlineSnapshot(`
[SB_SVELTE_CSF_PARSER_EXTRACT_SVELTE_0004 (MissingDefineMetaVariableDeclarationError): Stories file: <path not specified>
doesn't have 'defineMeta' call used for variable declaration inside the module tag ('<script context="module"> <!-- ... --> </script>').
Make sure this stories file has initial code snippet in order for this addon to work correctly:
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
const { Story } = defineMeta({});
</script>]
`);
});

@@ -38,3 +72,14 @@ it("fails when 'Story' is not destructured", ({ expect }) => {

});
expect(extractModuleNodes({ module })).rejects.toThrow();
expect(extractModuleNodes({ module })).rejects.toThrowErrorMatchingInlineSnapshot(`
[SB_SVELTE_CSF_PARSER_EXTRACT_SVELTE_0004 (MissingDefineMetaVariableDeclarationError): Stories file: <path not specified>
doesn't have 'defineMeta' call used for variable declaration inside the module tag ('<script context="module"> <!-- ... --> </script>').
Make sure this stories file has initial code snippet in order for this addon to work correctly:
<script context="module">
import { defineMeta } from "@storybook/addon-svelte-csf";
const { Story } = defineMeta({});
</script>]
`);
});

@@ -73,2 +118,3 @@ it('works when it has valid required entry snippet', ({ expect }) => {

expect(nodes.defineMetaImport).toBeDefined();
expect(nodes.defineMetaImport.imported.name).toBe('defineMeta');
expect(nodes.setTemplateImport).toBeDefined();

@@ -78,2 +124,3 @@ expect(nodes.setTemplateImport?.local.name).toBe('setTemplate');

expect(nodes.storyIdentifier).toBeDefined();
expect(nodes.storyIdentifier.name).toBe('Story');
});

@@ -80,0 +127,0 @@ it("works when 'setTemplate' is NOT used in stories", async ({ expect }) => {

/// <reference types="svelte" />
import type { Root } from 'svelte/compiler';
import { extractModuleNodes } from './module-nodes.js';
import { extractFragmentNodes } from './fragment-nodes.js';
import { extractInstanceNodes } from './instance-nodes.js';
import { extractModuleNodes } from './module-nodes';
import { extractFragmentNodes } from './fragment-nodes';
import { extractInstanceNodes } from './instance-nodes';
/**

@@ -7,0 +7,0 @@ * Selected nodes extracted from the Svelte AST via `svelte.compile`,

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

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

@@ -5,0 +5,0 @@ * Pick only required Svelte AST nodes for further usage in this addon.

import { describe, it } from 'vitest';
import { extractSvelteASTNodes } from './nodes.js';
import { getSvelteAST } from '../../../parser/ast.js';
import { extractSvelteASTNodes } from './nodes';
import { getSvelteAST } from '#parser/ast';
describe(extractSvelteASTNodes.name, () => {

@@ -5,0 +5,0 @@ it('works with a simple example', ({ expect }) => {

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

import { plugin } from './compiler/plugin.js';
import { indexer } from './indexer/index.js';
import { plugin } from './compiler/plugin';
import { indexer } from './indexer/index';
export const viteFinal = async (config) => {

@@ -4,0 +4,0 @@ return {

// WARN: Temporary location. Feel free to move if needed.
// It is likely this file will be removed if we successfully get rid of `StoriesExtractor`.
import { getContext, hasContext, setContext } from 'svelte';
import { storyNameToExportName } from '../../utils/identifier-utils.js';
import { storyNameToExportName } from '#utils/identifier-utils';
const CONTEXT_KEY = 'storybook-stories-extractor-context';

@@ -6,0 +6,0 @@ function buildContext(props) {

/// <reference types="svelte" />
import type { Meta, StoryObj, StoryContext } from '@storybook/svelte';
import type { Story } from '../../index.js';
import type Story from '#runtime/Story.svelte';
declare function buildContext<TMeta extends Meta>(): {

@@ -5,0 +5,0 @@ readonly template: ((this: void, args_0: StoryObj<TMeta>["args"], args_1: StoryContext<TMeta["args"]>) => typeof import("svelte").SnippetReturn & {

@@ -16,8 +16,7 @@ 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, codeByStoryMap: Record<string, string>) => 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) => 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, codeByStoryMap) => {
export const createRuntimeStories = (Stories, meta) => {
const repository = {

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

Stories,
code: codeByStoryMap[exportName],
storyContext,

@@ -52,0 +51,0 @@ args,

@@ -1,10 +0,19 @@

/// <reference types="svelte" />
import type { Meta, StoryContext, StoryObj } from '@storybook/svelte';
type Params<TMeta extends Meta> = {
import type { StoryContext, StoryObj } from '@storybook/svelte';
type Params = {
args: StoryObj['args'];
storyContext: StoryContext;
};
/**
* Given a code string representing the raw source code for the story,
* and the current, dynamic args
* this function:
* 1. Replaces args references in the code with the actual values
* 2. Emits the final code to Storybook's internal code provider
* So that it can be shown in source code viewer
*/
export declare const emitCode: (params: Params) => void;
export declare const generateCodeToEmit: ({ code, args }: {
code: string;
args: StoryObj<TMeta>['args'];
storyContext: StoryContext<TMeta['args']>;
};
export declare const emitCode: <TMeta extends import("@storybook/types").ComponentAnnotations<import("@storybook/svelte").SvelteRenderer<import("svelte").SvelteComponent<Record<string, any>, any, any>>, import("@storybook/types").Args>>(params: Params<TMeta>) => void;
export declare const generateCodeToEmit: ({ code, args }: Params<Meta>) => string;
args: StoryObj['args'];
}) => string;
export {};
import { SourceType, SNIPPET_RENDERED } from '@storybook/docs-tools';
import { addons } from '@storybook/preview-api';
import get from 'lodash-es/get';
const channel = addons.getChannel();
/**
* Given a code string representing the raw source code for the story,
* and the current, dynamic args
* this function:
* 1. Replaces args references in the code with the actual values
* 2. Emits the final code to Storybook's internal code provider
* So that it can be shown in source code viewer
*/
export const emitCode = (params) => {

@@ -9,3 +18,6 @@ const { storyContext } = params;

}
const codeToEmit = generateCodeToEmit(params);
const codeToEmit = generateCodeToEmit({
code: storyContext.parameters.__svelteCsf.rawCode,
args: params.args,
});
// Using setTimeout here to ensure we're emitting after the base @storybook/svelte emits its version of the code

@@ -21,6 +33,10 @@ // TODO: fix this in @storybook/svelte, don't emit when using stories.svelte files

};
// Copy from X
// Copied from @storybook/svelte at https://github.com/storybookjs/storybook/blob/17b7512c60256c739b890b3d85aaac992806dee6/code/renderers/svelte/src/docs/sourceDecorator.ts#L16-L33
const skipSourceRender = (context) => {
const sourceParams = context?.parameters.docs?.source;
const isArgsStory = context?.parameters.__isArgsStory;
const rawCode = context?.parameters.__svelteCsf.rawCode;
if (!rawCode) {
return true;
}
// always render if the user forces it

@@ -40,9 +56,44 @@ if (sourceParams?.type === SourceType.DYNAMIC) {

// make the props multiline if the string is longer than 50 chars
// TODO: do this at the final stage instead, taking into account the singular args replacements
if (allPropsString.length > 50) {
allPropsString = allPropsArray.join('\n ');
// TODO: the indendation only works if it's in the root-level component. In a nested component, the indentation will be too shallow
allPropsString = `\n ${allPropsArray.join('\n ')}\n`;
}
let codeToEmit = code.replaceAll('{...args}', allPropsString);
// TODO: replace singular args too.
let codeToEmit = code
.replaceAll('{...args}', allPropsString)
// replace single arg references with their actual value,
// eg. myProp={args.something} => myProp={"actual"}
// or <h1>{args.something}</h1> => <h1>"actual"</h1>
.replace(/args(?:[\w\d_$\.\?\[\]"'])+/g, (argPath) => {
const path = argPath.replaceAll('?', ''); // remove optional chaining character
const value = get({ args }, path);
return valueToString(value);
});
return codeToEmit;
};
const getFunctionName = (fn) => {
const name = fn.getMockName?.() ?? fn.name;
if (name && name !== 'spy') {
return name;
}
return '() => {}';
};
/**
* convert a value to a stringified version
*/
const valueToString = (value) => {
if (value[Symbol.for('svelte.snippet')]) {
return 'snippet';
}
if (typeof value === 'function') {
return getFunctionName(value);
}
return (JSON.stringify(value, null, 1)
?.replace(/\n/g, '')
// Find "}" or "]" at the end of the string, not preceded by a space, and add a space
.replace(/(?<!\s)([}\]])$/, ' $1'));
};
/**
* convert a {key: value} pair into Svelte attributes, eg. {someKey: "some string"} => someKey="some string"
*/
const argsToProps = (key, value) => {

@@ -55,10 +106,7 @@ if (value === undefined || value === null) {

}
const stringValue = valueToString(value);
if (typeof value === 'string') {
return `${key}=${JSON.stringify(value)}`;
return `${key}=${stringValue}`;
}
if (typeof value === 'function') {
return `${key}={${value.getMockName?.() ?? value.name ?? '() => {}'}}`;
}
// TODO: add space at the end of objects, multiline long values
return `${key}={${JSON.stringify(value, null, 1).replace(/\n/g, '')}}`;
return `${key}={${stringValue}}`;
};
import { SvelteComponent } from "svelte";
import type { Meta } from '@storybook/svelte';
import type { ComponentType } from 'svelte';
import { type StoriesRepository } from './contexts/extractor.svelte.js';
declare class __sveltets_Render<Component extends SvelteComponent> {
props(): {
Stories: Component extends SvelteComponent<Record<string, any>, any, any> ? ComponentType<Component> : never;
repository: () => StoriesRepository<Meta<Component>>;
repository: () => StoriesRepository<Meta<Component_1>>;
};

@@ -10,0 +9,0 @@ events(): {} & {

@@ -16,14 +16,14 @@ import { SvelteComponent } from "svelte";

/**
* Name of the story. Can be omitted if `exportName` is provided.
*/
* Name of the story. Can be omitted if `exportName` is provided.
*/
name?: string | undefined;
/**
* exportName of the story.
* If not provided, it will be generated from the 'name', by converting it to a valid, PascalCased JS variable name.
* eg. 'My story!' -> 'MyStory'
*
* Use this prop to explicitly set the export name of the story. This is useful if you have multiple stories with the names
* that result in duplicate export names like "My story" and "My story!".
* It's also useful for explicitly defining the export that can be imported in MDX docs.
*/
* exportName of the story.
* If not provided, it will be generated from the 'name', by converting it to a valid, PascalCased JS variable name.
* eg. 'My story!' -> 'MyStory'
*
* Use this prop to explicitly set the export name of the story. This is useful if you have multiple stories with the names
* that result in duplicate export names like "My story" and "My story!".
* It's also useful for explicitly defining the export that can be imported in MDX docs.
*/
exportName?: string | undefined;

@@ -37,19 +37,14 @@ /**

* @deprecated
* Is recommended to use `parameters={{ docs: { source: { code: "..." } } }}` instead, to follow Regular CSF.
* WARNING: This is under consideration to be revamped.
* Ref: https://github.com/storybookjs/addon-svelte-csf/pull/181#issuecomment-2130744977
*
* Specify which sources should be shown.
*
* 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.
* If source is a string, it replaces the source of the story.
* Use `parameters={{ docs: { source: { code: "..." } } }}` instead.
*/
source?: string | boolean | undefined;
} & StoryObj<TMeta>;
source?: undefined;
/**
* The args for the story
*/
args?: (StoryObj<TMeta>["args"] extends infer T ? { [ArgKey in keyof T]: StoryObj<TMeta>["args"][ArgKey] extends (this: void) => typeof import("svelte").SnippetReturn & {
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
} ? string | number | boolean | void | ((this: void) => typeof import("svelte").SnippetReturn & {
_: "functions passed to {@render ...} tags must use the `Snippet` type imported from \"svelte\"";
}) : StoryObj<TMeta>["args"][ArgKey]; } : never) | undefined;
} & Omit<StoryObj<TMeta>, "args">;
events(): {} & {

@@ -56,0 +51,0 @@ [evt: string]: CustomEvent<any>;

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

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

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

{
"name": "@storybook/addon-svelte-csf",
"version": "5.0.0--canary.181.363788a.0",
"version": "5.0.0--canary.181.3f35763.0",
"description": "Allows to write stories in Svelte syntax",

@@ -17,2 +17,5 @@ "keywords": [

"packageManager": "pnpm@9.1.3+sha512.7c2ea089e1a6af306409c4fc8c4f0897bdac32b772016196c469d9428f1fe2d5a21daf8ad6512762654ac645b5d9136bb210ec9a00afa8dbc4677843ba362ecd",
"imports": {
"#*": "./src/*.ts"
},
"exports": {

@@ -44,2 +47,3 @@ ".": {

"test:watch": "vitest watch",
"format": "prettier --write .",
"chromatic": "chromatic --exit-zero-on-changes",

@@ -58,4 +62,2 @@ "coverage": "vitest run --coverage",

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

@@ -90,2 +92,5 @@ },

"jsdom": "^24.1.0",
"lodash-es": "^4.17.21",
"prettier": "^3.3.1",
"prettier-plugin-svelte": "^3.2.4",
"rimraf": "^5.0.7",

@@ -96,3 +101,3 @@ "rollup": "^4.18.0",

"svelte-check": "^3.5.0",
"typescript": "^5.1.6",
"typescript": "^5.4.5",
"vite": "^5.2.10",

@@ -99,0 +104,0 @@ "vite-plugin-inspect": "^0.8.4",

@@ -23,2 +23,6 @@ # Svelte Story Format

<button class="button" class:rounded on:click={onClick}>
<slot />
</button>
<style>

@@ -36,6 +40,2 @@ .rounded {

</style>
<button class="button" class:rounded on:click={onClick}>
<slot />
</button>
```

@@ -50,5 +50,5 @@

export const meta = {
title: "Button",
component: Button
}
title: 'Button',
component: Button,
};
</script>

@@ -67,5 +67,3 @@

<!--👇 'on:click' allows to forward event to addon-actions -->
<Button {...args}
on:click
on:click={handleClick}>
<Button {...args} on:click on:click={handleClick}>
You clicked: {count}

@@ -75,5 +73,5 @@ </Button>

<Story name="Rounded" args={{rounded: true}}/>
<Story name="Rounded" args={{ rounded: true }} />
<Story name="Square" source args={{rounded: false}}/>
<Story name="Square" source args={{ rounded: false }} />

@@ -88,3 +86,2 @@ <!-- Dynamic snippet should be disabled for this story -->

## Getting Started

@@ -94,3 +91,3 @@

2. In `.storybook/main.js`, add `@storybook/addon-svelte-csf` to the addons array
4. In `.storybook/main.js`, include .stories.svelte files in your stories patterns, eg. by changing the patterns to `'../src/**/*.stories.@(js|jsx|ts|tsx|svelte)'`
3. In `.storybook/main.js`, include .stories.svelte files in your stories patterns, eg. by changing the patterns to `'../src/**/*.stories.@(js|jsx|ts|tsx|svelte)'`

@@ -97,0 +94,0 @@ An example `main.js` configuration could look like this:

Sorry, the diff of this file is not supported yet

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