generated from storybookjs/addon-kit
-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
17 changed files
with
329 additions
and
246 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { compile, type Root } from 'svelte/compiler'; | ||
|
||
export function getAST(source: string) { | ||
const { ast }: { ast: Root } = compile(source, { modernAst: true }); | ||
|
||
return ast; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import type { Script } from 'svelte/compiler'; | ||
|
||
import type { AddonASTNodes } from './types.js'; | ||
import { walkOnModule } from './walkers/module.js'; | ||
|
||
/** | ||
* Pick only required AST nodes for further usage in this addon. | ||
*/ | ||
export function extractASTNodes(module: Script | null): AddonASTNodes { | ||
if (!module) { | ||
throw new Error(`The stories file must have a module tag ('<script context="module">').`); | ||
} | ||
|
||
return walkOnModule(module); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,31 @@ | ||
import { compile, type Root } from 'svelte/compiler'; | ||
import type { Fragment } from 'svelte/compiler'; | ||
|
||
import type { StoriesFileMeta } from './types.js'; | ||
import { walkOnModule } from './walkers/module.js'; | ||
import type { AddonASTNodes, StoriesFileMeta } from './types.js'; | ||
import { walkOnFragment } from './walkers/fragment.js'; | ||
import { walkOnDefineMeta } from './walkers/define-meta.js'; | ||
|
||
/** | ||
* Parse raw stories file component in Svelte format, | ||
* and extract the most stories file meta, | ||
* which are required to generate `StoryFn's` for `@storybook/svelte` components. | ||
*/ | ||
export function extractStories(rawSource: string): StoriesFileMeta { | ||
const { ast }: { ast: Root } = compile(rawSource, { modernAst: true }); | ||
const { module, fragment } = ast; | ||
|
||
const moduleMeta = walkOnModule(module); | ||
const fragmentMeta = walkOnFragment({ | ||
export function extractStories({ | ||
nodes, | ||
fragment, | ||
source, | ||
}: { | ||
nodes: AddonASTNodes; | ||
fragment: Fragment; | ||
source: string; | ||
}): StoriesFileMeta { | ||
const { stories } = walkOnFragment({ | ||
fragment, | ||
rawSource, | ||
addonComponentName: moduleMeta.addonComponentName, | ||
source: source, | ||
nodes, | ||
}); | ||
|
||
return { | ||
module: moduleMeta, | ||
fragment: fragmentMeta, | ||
defineMeta: walkOnDefineMeta(nodes), | ||
stories, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { logger } from '@storybook/client-logger'; | ||
import dedent from 'dedent'; | ||
import type { SvelteNode } from 'svelte/compiler'; | ||
import type { ObjectExpression, Property } from 'estree'; | ||
import { walk, type Visitors } from 'zimmerframe'; | ||
|
||
import type { AddonASTNodes, DefineMeta } from '../types.js'; | ||
|
||
export function walkOnDefineMeta(nodes: AddonASTNodes): DefineMeta { | ||
const state: Partial<DefineMeta> = {}; | ||
const visitors: Visitors<SvelteNode, typeof state> = { | ||
// Walk on `const { ... } = defineMeta()` | ||
VariableDeclaration(node, { state, visit }) { | ||
const { declarations, leadingComments } = node; | ||
|
||
if (leadingComments) { | ||
state.description = dedent(leadingComments[0].value.replaceAll(/^ *\*/gm, '')); | ||
} | ||
|
||
const declaration = declarations[0]; | ||
const { id, init } = declaration; | ||
|
||
if ( | ||
id.type === 'ObjectPattern' && | ||
init?.type === 'CallExpression' && | ||
init.callee.type === 'Identifier' | ||
) { | ||
visit(init, state); | ||
} | ||
}, | ||
|
||
// Walk on `defineMeta` function call - called by `visit()` from `VariableDeclarator` | ||
CallExpression(node, { state, stop }) { | ||
if (node.arguments.length !== 1) { | ||
throw new Error( | ||
`Function '${nodes.defineMetaImport.local.name}({ ... })' takes 1 argument only` | ||
); | ||
} | ||
|
||
if (node.arguments[0].type !== 'ObjectExpression') { | ||
throw new Error( | ||
`Function '${nodes.defineMetaImport?.local.name}({ ... })' takes an object literal which satisfies Meta` | ||
); | ||
} | ||
|
||
const { properties } = node.arguments[0]; | ||
|
||
state.id = getString('id', properties); | ||
state.title = getString('title', properties); | ||
state.tags = getTags(properties); | ||
|
||
stop(); | ||
}, | ||
}; | ||
|
||
walk(nodes.defineMetaVar, state, visitors); | ||
|
||
return state; | ||
} | ||
|
||
function getString(propertyName: string, properties: ObjectExpression['properties']) { | ||
const property = lookupProperty(propertyName, properties); | ||
|
||
if (property && property.value.type === 'Literal') { | ||
const { value } = property.value; | ||
|
||
if (value) { | ||
if (typeof value === 'string') { | ||
return value; | ||
} | ||
|
||
throw new Error(`'meta.${propertyName}' should be a string, found ${typeof value}`); | ||
} | ||
} | ||
} | ||
|
||
function getTags(properties: ObjectExpression['properties']) { | ||
const tags = lookupProperty('tags', properties); | ||
|
||
if (tags) { | ||
const { value } = tags; | ||
|
||
if (value.type === 'ArrayExpression') { | ||
return value.elements.map((item) => { | ||
if (item?.type === 'Literal') { | ||
if (typeof item.value !== 'string') { | ||
throw Error(`'meta.tags' should be an array of strings.`); | ||
} | ||
|
||
return item.value; | ||
} | ||
|
||
throw Error(`'meta.tags' should be an array of strings.`); | ||
}); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* WARN: Potential issue, some of the properites might be extended by `SpreadElement`. | ||
* I couldn't think of the case how it could be ever reached, if the code was already "compiled"(?). | ||
* I'll leave a warning, to avoid confusion during usage, and in case it actually happens. | ||
*/ | ||
function lookupProperty(name: string, properties: ObjectExpression['properties']) { | ||
return properties.find((p) => { | ||
if (p.type === 'SpreadElement') { | ||
logger.warn( | ||
`Spread operator is not supported in the 'defineMeta' literal object. Please file an issue with an use case.` | ||
); | ||
} | ||
|
||
return p.type === 'Property' && p.key.type === 'Identifier' && p.key.name === name; | ||
}) as Property | undefined; | ||
} |
Oops, something went wrong.