From 12e463340765704d87170a85afaf03a8af1ce71a Mon Sep 17 00:00:00 2001 From: Jonah Henriksson <33059163+JonahPlusPlus@users.noreply.github.com> Date: Wed, 13 Nov 2024 12:12:55 -0500 Subject: [PATCH] 1.0.0-beta.6: Improve code snippets and CD (#16) * Improve code snippets * Update solid.yml * Fix CD expressions * Generate empty args for render function * Update versions --- .github/workflows/solid-vite.yml | 2 + .github/workflows/solid.yml | 6 +- packages/frameworks/solid-vite/package.json | 2 +- packages/renderers/solid/package.json | 2 +- .../solid/src/docs/sourceDecorator.test.tsx | 71 +++++++++++ .../solid/src/docs/sourceDecorator.tsx | 117 ++++++++++++++++-- 6 files changed, 182 insertions(+), 18 deletions(-) diff --git a/.github/workflows/solid-vite.yml b/.github/workflows/solid-vite.yml index 74dee7e..90d9853 100644 --- a/.github/workflows/solid-vite.yml +++ b/.github/workflows/solid-vite.yml @@ -6,12 +6,14 @@ on: description: Publish? required: false type: boolean + default: false workflow_dispatch: inputs: publish: description: Publish? required: false type: boolean + default: false defaults: run: working-directory: packages/frameworks/solid-vite diff --git a/.github/workflows/solid.yml b/.github/workflows/solid.yml index a848483..7f839eb 100644 --- a/.github/workflows/solid.yml +++ b/.github/workflows/solid.yml @@ -12,14 +12,13 @@ on: - packages/renderers/solid/** release: types: [published] - branches: - - main workflow_dispatch: inputs: publish: description: Publish? required: false type: boolean + default: false defaults: run: working-directory: packages/renderers/solid @@ -43,4 +42,5 @@ jobs: needs: solid-workflow uses: ./.github/workflows/solid-vite.yml with: - publish: ${{ github.event_name == 'release' || inputs.publish }} + publish: ${{ (github.event_name == 'release') || (inputs.publish == true) }} + secrets: inherit diff --git a/packages/frameworks/solid-vite/package.json b/packages/frameworks/solid-vite/package.json index ad160d0..cc1aaf6 100644 --- a/packages/frameworks/solid-vite/package.json +++ b/packages/frameworks/solid-vite/package.json @@ -1,7 +1,7 @@ { "name": "storybook-solidjs-vite", "type": "module", - "version": "1.0.0-beta.5", + "version": "1.0.0-beta.6", "description": "Storybook for SolidJS and Vite: Develop SolidJS in isolation with Hot Reloading.", "keywords": [ "storybook" diff --git a/packages/renderers/solid/package.json b/packages/renderers/solid/package.json index 5445aa9..4250087 100644 --- a/packages/renderers/solid/package.json +++ b/packages/renderers/solid/package.json @@ -1,7 +1,7 @@ { "name": "storybook-solidjs", "type": "module", - "version": "1.0.0-beta.5", + "version": "1.0.0-beta.6", "description": "Storybook SolidJS renderer", "keywords": [ "storybook" diff --git a/packages/renderers/solid/src/docs/sourceDecorator.test.tsx b/packages/renderers/solid/src/docs/sourceDecorator.test.tsx index 3a2f28e..ac50768 100644 --- a/packages/renderers/solid/src/docs/sourceDecorator.test.tsx +++ b/packages/renderers/solid/src/docs/sourceDecorator.test.tsx @@ -74,6 +74,77 @@ test('component with typescript', () => { `); }); +test('component with expression children', () => { + const newSrc = generateSolidSource( + 'Component', + '{ args: { children: { do: () => { return 32; } } } }', + ); + + expect(newSrc).toMatchInlineSnapshot(` + "{{ + do: () => { + return 32; + } + }}" + `); +}); + +test('component with render function', () => { + const newSrc = generateSolidSource( + 'Component', + '{ render: () => Hello }', + ); + + expect(newSrc).toMatchInlineSnapshot( + `"Hello"`, + ); +}); + +test('component with render function and args', () => { + const newSrc = generateSolidSource( + 'Component', + '{ args: { foo: 32 }, render: (args) => Hello }', + ); + + expect(newSrc).toMatchInlineSnapshot(` + "const args = { + foo: 32 + }; + + Hello" + `); +}); + +test('component with render function and missing args', () => { + const newSrc = generateSolidSource( + 'Component', + '{ render: (args) => Hello }', + ); + + expect(newSrc).toMatchInlineSnapshot(` + "const args = {}; + + Hello" + `); +}); + +test('component with render function and args and ctx', () => { + const newSrc = generateSolidSource( + 'Component', + '{ args: { foo: 32 }, render: (args, ctx) => Hello }', + ); + + expect(newSrc).toMatchInlineSnapshot(` + "const args = { + foo: 32 + }; + + var ctx; + + Hello" + `); +}); + test('component missing story config', () => { const newSrc = () => generateSolidSource('Component', '5 + 4'); diff --git a/packages/renderers/solid/src/docs/sourceDecorator.tsx b/packages/renderers/solid/src/docs/sourceDecorator.tsx index 68e711d..896d7af 100644 --- a/packages/renderers/solid/src/docs/sourceDecorator.tsx +++ b/packages/renderers/solid/src/docs/sourceDecorator.tsx @@ -84,7 +84,65 @@ export const sourceDecorator = ( * Generate Solid JSX from story source. */ export function generateSolidSource(name: string, src: string): string { - const { attributes, children } = parseProps(src); + const ast = parser.parseExpression(src, { plugins: ['jsx', 'typescript'] }); + const { attributes, children, original } = parseArgs(ast); + const render = parseRender(ast); + + // If there is a render function, display it to the best of our ability. + if (render) { + const { body, params } = render; + let newSrc = ''; + + // Add arguments declaration. + if (params[0]) { + const args = original ?? { + type: 'ObjectExpression', + properties: [], + }; + + const argsStatement = { + type: 'VariableDeclaration', + kind: 'const', + declarations: [ + { + type: 'VariableDeclarator', + id: { + type: 'Identifier', + name: params[0], + }, + init: args, + }, + ], + }; + + newSrc += generate(argsStatement, { compact: false }).code + '\n\n'; + } + + // Add context declaration. + if (params[1]) { + const ctxStatement = { + type: 'VariableDeclaration', + kind: 'var', + declarations: [ + { + type: 'VariableDeclarator', + id: { + type: 'Identifier', + name: params[1], + }, + }, + ], + }; + + newSrc += generate(ctxStatement, { compact: false }).code + '\n\n'; + } + + newSrc += generate(body, { compact: false }).code; + + return newSrc; + } + + // Otherwise, render a component with the arguments. const selfClosing = children == null || children.length == 0; @@ -138,49 +196,82 @@ function toJSXChild(node: any): object { if (t.isExpression(node)) { return { type: 'JSXExpressionContainer', - value: node, + expression: node, }; } return { type: 'JSXExpressionContainer', - value: t.jsxEmptyExpression(), + expression: t.jsxEmptyExpression(), + }; +} +/** Story render function. */ +interface SolidRender { + body: object; + params: string[]; +} + +function parseRender(ast: any): SolidRender | null { + if (ast.type != 'ObjectExpression') throw 'Expected `ObjectExpression` type'; + // Find render property. + const renderProp = ast.properties.find((v: any) => { + if (v.type != 'ObjectProperty') return false; + if (v.key.type != 'Identifier') return false; + return v.key.name == 'render'; + }) as any | undefined; + if (!renderProp) return null; + + const renderFn = renderProp.value; + if ( + renderFn.type != 'ArrowFunctionExpression' && + renderFn.type != 'FunctionExpression' + ) { + console.warn('`render` property is not a function, skipping...'); + return null; + } + + return { + body: renderFn.body, + params: renderFn.params.map((x: any) => x.name), }; } -interface SolidProps { +/** Story arguments. */ +interface SolidArgs { attributes: object[]; children: object[] | null; + original: object | null; } /** - * Parses component properties from source expression. + * Parses component arguments from source expression. * * The source code will be in the form of a `Story` object. */ -function parseProps(src: string): SolidProps { - const ast = parser.parseExpression(src, { plugins: ['jsx', 'typescript'] }); +function parseArgs(ast: any): SolidArgs { if (ast.type != 'ObjectExpression') throw 'Expected `ObjectExpression` type'; // Find args property. - const args_prop = ast.properties.find((v: any) => { + const argsProp = ast.properties.find((v: any) => { if (v.type != 'ObjectProperty') return false; if (v.key.type != 'Identifier') return false; return v.key.name == 'args'; }) as any | undefined; // No args, so there aren't any properties or children. - if (!args_prop) + if (!argsProp) return { attributes: [], children: null, + original: null, }; // Get arguments. - const args = args_prop.value; - if (args.type != 'ObjectExpression') throw 'Expected `ObjectExpression` type'; + const original = argsProp.value; + if (original.type != 'ObjectExpression') + throw 'Expected `ObjectExpression` type'; // Construct props object, where values are source code slices. const attributes: object[] = []; let children: object[] | null = null; - for (const el of args.properties) { + for (const el of original.properties) { let attr: object | null = null; switch (el.type) { @@ -210,7 +301,7 @@ function parseProps(src: string): SolidProps { } } - return { attributes, children }; + return { attributes, children, original }; } /**