diff --git a/.circleci/config.yml b/.circleci/config.yml index 9b03dd3739bc..2273bcc5bdf3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -609,22 +609,22 @@ workflows: requires: - build - create-sandboxes: - parallelism: 34 + parallelism: 36 requires: - build # - smoke-test-sandboxes: # disabled for now # requires: # - create-sandboxes - build-sandboxes: - parallelism: 34 + parallelism: 36 requires: - create-sandboxes - chromatic-sandboxes: - parallelism: 31 + parallelism: 33 requires: - build-sandboxes - e2e-production: - parallelism: 31 + parallelism: 33 requires: - build-sandboxes - e2e-dev: @@ -632,7 +632,7 @@ workflows: requires: - create-sandboxes - test-runner-production: - parallelism: 31 + parallelism: 33 requires: - build-sandboxes # TODO: reenable once we find out the source of flakyness diff --git a/.github/workflows/generate-sandboxes-main.yml b/.github/workflows/generate-sandboxes-main.yml index a99b2288cf0c..7d9328578493 100644 --- a/.github/workflows/generate-sandboxes-main.yml +++ b/.github/workflows/generate-sandboxes-main.yml @@ -19,12 +19,12 @@ jobs: YARN_ENABLE_IMMUTABLE_INSTALLS: false CLEANUP_SANDBOX_NODE_MODULES: true steps: - - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - uses: actions/checkout@v3 with: ref: main + - uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' - name: Setup git user run: | git config --global user.name "Storybook Bot" @@ -43,7 +43,7 @@ jobs: run: yarn wait-on http://localhost:6001 working-directory: ./code - name: Generate - run: yarn generate-sandboxes --local-registry --exclude=angular-cli/prerelease + run: yarn generate-sandboxes --local-registry working-directory: ./code - name: Publish run: yarn publish-sandboxes --remote=https://storybook-bot:${{ secrets.PAT_STORYBOOK_BOT}}@github.com/storybookjs/sandboxes.git --push --branch=main diff --git a/.github/workflows/generate-sandboxes-next.yml b/.github/workflows/generate-sandboxes-next.yml index 91515e0862e9..8a28c4cbb23a 100644 --- a/.github/workflows/generate-sandboxes-next.yml +++ b/.github/workflows/generate-sandboxes-next.yml @@ -19,12 +19,12 @@ jobs: YARN_ENABLE_IMMUTABLE_INSTALLS: false CLEANUP_SANDBOX_NODE_MODULES: true steps: - - uses: actions/setup-node@v3 - with: - node-version-file: '.nvmrc' - uses: actions/checkout@v3 with: ref: next + - uses: actions/setup-node@v3 + with: + node-version-file: '.nvmrc' - name: Setup git user run: | git config --global user.name "Storybook Bot" @@ -43,7 +43,7 @@ jobs: run: yarn wait-on http://localhost:6001 working-directory: ./code - name: Generate - run: yarn generate-sandboxes --local-registry --exclude=angular-cli/prerelease --debug + run: yarn generate-sandboxes --local-registry --debug working-directory: ./code - name: Publish run: yarn publish-sandboxes --remote=https://storybook-bot:${{ secrets.PAT_STORYBOOK_BOT}}@github.com/storybookjs/sandboxes.git --push --branch=next diff --git a/code/builders/builder-vite/package.json b/code/builders/builder-vite/package.json index 61231c2eb352..c55075c292aa 100644 --- a/code/builders/builder-vite/package.json +++ b/code/builders/builder-vite/package.json @@ -72,7 +72,7 @@ "peerDependencies": { "@preact/preset-vite": "*", "typescript": ">= 4.3.x", - "vite": "^3.0.0 || ^4.0.0", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0", "vite-plugin-glimmerx": "*" }, "peerDependenciesMeta": { diff --git a/code/builders/builder-vite/src/build.ts b/code/builders/builder-vite/src/build.ts index 0a75cc71b3a2..67da9f989692 100644 --- a/code/builders/builder-vite/src/build.ts +++ b/code/builders/builder-vite/src/build.ts @@ -1,10 +1,10 @@ -import { build as viteBuild, mergeConfig } from 'vite'; import type { Options } from '@storybook/types'; import { commonConfig } from './vite-config'; import { sanitizeEnvVars } from './envs'; export async function build(options: Options) { + const { build: viteBuild, mergeConfig } = await import('vite'); const { presets } = options; const config = await commonConfig(options, 'build'); @@ -21,6 +21,5 @@ export async function build(options: Options) { }).build; const finalConfig = await presets.apply('viteFinal', config, options); - await viteBuild(await sanitizeEnvVars(options, finalConfig)); } diff --git a/code/builders/builder-vite/src/codegen-entries.ts b/code/builders/builder-vite/src/codegen-entries.ts index 6a6328cf8f96..44c3163b1def 100644 --- a/code/builders/builder-vite/src/codegen-entries.ts +++ b/code/builders/builder-vite/src/codegen-entries.ts @@ -1,15 +1,19 @@ import { loadPreviewOrConfigFile } from '@storybook/core-common'; import type { Options } from '@storybook/types'; import slash from 'slash'; -import { normalizePath } from 'vite'; import { listStories } from './list-stories'; -const absoluteFilesToImport = (files: string[], name: string) => +const absoluteFilesToImport = async ( + files: string[], + name: string, + normalizePath: (id: string) => string +) => files .map((el, i) => `import ${name ? `* as ${name}_${i} from ` : ''}'/@fs/${normalizePath(el)}'`) .join('\n'); export async function generateVirtualStoryEntryCode(options: Options) { + const { normalizePath } = await import('vite'); const storyEntries = await listStories(options); const resolveMap = storyEntries.reduce>( (prev, entry) => ({ ...prev, [entry]: entry.replace(slash(process.cwd()), '.') }), @@ -18,7 +22,7 @@ export async function generateVirtualStoryEntryCode(options: Options) { const modules = storyEntries.map((entry, i) => `${JSON.stringify(entry)}: story_${i}`).join(','); return ` - ${absoluteFilesToImport(storyEntries, 'story')} + ${await absoluteFilesToImport(storyEntries, 'story', normalizePath)} function loadable(key) { return {${modules}}[key]; diff --git a/code/builders/builder-vite/src/codegen-importfn-script.ts b/code/builders/builder-vite/src/codegen-importfn-script.ts index f81c4647f641..5df14d875f25 100644 --- a/code/builders/builder-vite/src/codegen-importfn-script.ts +++ b/code/builders/builder-vite/src/codegen-importfn-script.ts @@ -1,5 +1,5 @@ import * as path from 'path'; -import { normalizePath } from 'vite'; + import type { Options } from '@storybook/types'; import { logger } from '@storybook/node-logger'; @@ -26,6 +26,7 @@ function toImportPath(relativePath: string) { * @param stories An array of absolute story paths. */ async function toImportFn(stories: string[]) { + const { normalizePath } = await import('vite'); const objectEntries = stories.map((file) => { const ext = path.extname(file); const relativePath = normalizePath(path.relative(process.cwd(), file)); diff --git a/code/builders/builder-vite/src/list-stories.ts b/code/builders/builder-vite/src/list-stories.ts index 746373c4c0f5..521952bff15a 100644 --- a/code/builders/builder-vite/src/list-stories.ts +++ b/code/builders/builder-vite/src/list-stories.ts @@ -4,9 +4,10 @@ import { glob } from 'glob'; import { normalizeStories, commonGlobOptions } from '@storybook/core-common'; import type { Options } from '@storybook/types'; -import { normalizePath } from 'vite'; export async function listStories(options: Options) { + const { normalizePath } = await import('vite'); + return ( await Promise.all( normalizeStories(await options.presets.apply('stories', [], options), { diff --git a/code/builders/builder-vite/src/optimizeDeps.ts b/code/builders/builder-vite/src/optimizeDeps.ts index 43c64f34a7e4..ddb32c800403 100644 --- a/code/builders/builder-vite/src/optimizeDeps.ts +++ b/code/builders/builder-vite/src/optimizeDeps.ts @@ -1,5 +1,4 @@ import * as path from 'path'; -import { normalizePath, resolveConfig } from 'vite'; import type { InlineConfig as ViteInlineConfig, UserConfig } from 'vite'; import type { Options } from '@storybook/types'; import { listStories } from './list-stories'; @@ -128,6 +127,7 @@ const asyncFilter = async (arr: string[], predicate: (val: string) => Promise normalizePath(path.relative(root, storyPath))); // TODO: check if resolveConfig takes a lot of time, possible optimizations here diff --git a/code/builders/builder-vite/src/plugins/external-globals-plugin.ts b/code/builders/builder-vite/src/plugins/external-globals-plugin.ts index dd53ab4a4e56..1debc299aa99 100644 --- a/code/builders/builder-vite/src/plugins/external-globals-plugin.ts +++ b/code/builders/builder-vite/src/plugins/external-globals-plugin.ts @@ -3,7 +3,6 @@ import findCacheDirectory from 'find-cache-dir'; import { init, parse } from 'es-module-lexer'; import MagicString from 'magic-string'; import { ensureFile, writeFile } from 'fs-extra'; -import { mergeAlias } from 'vite'; import type { Alias, Plugin } from 'vite'; const escapeKeys = (key: string) => key.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'); @@ -38,6 +37,8 @@ const replacementMap = new Map([ */ export async function externalGlobalsPlugin(externals: Record) { await init; + const { mergeAlias } = await import('vite'); + return { name: 'storybook:external-globals-plugin', enforce: 'post', diff --git a/code/builders/builder-vite/src/plugins/inject-export-order-plugin.ts b/code/builders/builder-vite/src/plugins/inject-export-order-plugin.ts index 743043f7427c..621dae80e647 100644 --- a/code/builders/builder-vite/src/plugins/inject-export-order-plugin.ts +++ b/code/builders/builder-vite/src/plugins/inject-export-order-plugin.ts @@ -1,32 +1,35 @@ import { parse } from 'es-module-lexer'; import MagicString from 'magic-string'; -import { createFilter } from 'vite'; -const include = [/\.stories\.([tj])sx?$/, /(stories|story).mdx$/]; -const filter = createFilter(include); +export async function injectExportOrderPlugin() { + const { createFilter } = await import('vite'); -export const injectExportOrderPlugin = { - name: 'storybook:inject-export-order-plugin', - // This should only run after the typescript has been transpiled - enforce: 'post', - async transform(code: string, id: string) { - if (!filter(id)) return undefined; + const include = [/\.stories\.([tj])sx?$/, /(stories|story).mdx$/]; + const filter = createFilter(include); - // TODO: Maybe convert `injectExportOrderPlugin` to function that returns object, - // and run `await init;` once and then call `parse()` without `await`, - // instead of calling `await parse()` every time. - const [, exports] = await parse(code); + return { + name: 'storybook:inject-export-order-plugin', + // This should only run after the typescript has been transpiled + enforce: 'post', + async transform(code: string, id: string) { + if (!filter(id)) return undefined; - if (exports.includes('__namedExportsOrder')) { - // user has defined named exports already - return undefined; - } - const s = new MagicString(code); - const orderedExports = exports.filter((e) => e !== 'default'); - s.append(`;export const __namedExportsOrder = ${JSON.stringify(orderedExports)};`); - return { - code: s.toString(), - map: s.generateMap({ hires: true, source: id }), - }; - }, -}; + // TODO: Maybe convert `injectExportOrderPlugin` to function that returns object, + // and run `await init;` once and then call `parse()` without `await`, + // instead of calling `await parse()` every time. + const [, exports] = await parse(code); + + if (exports.includes('__namedExportsOrder')) { + // user has defined named exports already + return undefined; + } + const s = new MagicString(code); + const orderedExports = exports.filter((e) => e !== 'default'); + s.append(`;export const __namedExportsOrder = ${JSON.stringify(orderedExports)};`); + return { + code: s.toString(), + map: s.generateMap({ hires: true, source: id }), + }; + }, + }; +} diff --git a/code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts b/code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts index 46ca7045e6d5..c249fc523d1a 100644 --- a/code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts +++ b/code/builders/builder-vite/src/plugins/strip-story-hmr-boundaries.ts @@ -1,5 +1,4 @@ import type { Plugin } from 'vite'; -import { createFilter } from 'vite'; import MagicString from 'magic-string'; /** @@ -7,7 +6,9 @@ import MagicString from 'magic-string'; * as hmr boundaries, but vite has a bug which causes them to be treated as boundaries * (https://github.com/vitejs/vite/issues/9869). */ -export function stripStoryHMRBoundary(): Plugin { +export async function stripStoryHMRBoundary(): Promise { + const { createFilter } = await import('vite'); + const filter = createFilter(/\.stories\.([tj])sx?$/); return { name: 'storybook:strip-hmr-boundary-plugin', diff --git a/code/builders/builder-vite/src/vite-config.ts b/code/builders/builder-vite/src/vite-config.ts index 75778971b26a..24db49249909 100644 --- a/code/builders/builder-vite/src/vite-config.ts +++ b/code/builders/builder-vite/src/vite-config.ts @@ -1,5 +1,4 @@ import * as path from 'path'; -import { loadConfigFromFile, mergeConfig } from 'vite'; import findCacheDirectory from 'find-cache-dir'; import type { ConfigEnv, @@ -41,6 +40,8 @@ export async function commonConfig( _type: PluginConfigType ): Promise { const configEnv = _type === 'development' ? configEnvServe : configEnvBuild; + const { loadConfigFromFile, mergeConfig } = await import('vite'); + const { viteConfigPath } = await getBuilderOptions(options); const projectRoot = path.resolve(options.configDir, '..'); @@ -80,8 +81,8 @@ export async function pluginConfig(options: Options) { const plugins = [ codeGeneratorPlugin(options), await csfPlugin(options), - injectExportOrderPlugin, - stripStoryHMRBoundary(), + await injectExportOrderPlugin(), + await stripStoryHMRBoundary(), { name: 'storybook:allow-storybook-dir', enforce: 'post', diff --git a/code/builders/builder-vite/src/vite-server.ts b/code/builders/builder-vite/src/vite-server.ts index 13489d8580e5..ce4631cabaed 100644 --- a/code/builders/builder-vite/src/vite-server.ts +++ b/code/builders/builder-vite/src/vite-server.ts @@ -1,5 +1,4 @@ import type { Server } from 'http'; -import { createServer } from 'vite'; import type { Options } from '@storybook/types'; import { commonConfig } from './vite-config'; import { getOptimizeDeps } from './optimizeDeps'; @@ -29,5 +28,6 @@ export async function createViteServer(options: Options, devServer: Server) { const finalConfig = await presets.apply('viteFinal', config, options); + const { createServer } = await import('vite'); return createServer(await sanitizeEnvVars(options, finalConfig)); } diff --git a/code/e2e-tests/framework-svelte.spec.ts b/code/e2e-tests/framework-svelte.spec.ts new file mode 100644 index 000000000000..007f1182c781 --- /dev/null +++ b/code/e2e-tests/framework-svelte.spec.ts @@ -0,0 +1,40 @@ +/* eslint-disable jest/no-disabled-tests */ +import { test, expect } from '@playwright/test'; +import process from 'process'; +import { SbPage } from './util'; + +const storybookUrl = process.env.STORYBOOK_URL || 'http://localhost:6006'; +const templateName = process.env.STORYBOOK_TEMPLATE_NAME; + +test.describe('Svelte', () => { + test.skip( + // eslint-disable-next-line jest/valid-title + !templateName?.includes('svelte'), + 'Only run this test on Svelte' + ); + + test.beforeEach(async ({ page }) => { + await page.goto(storybookUrl); + await new SbPage(page).waitUntilLoaded(); + }); + + test('JS story has auto-generated args table', async ({ page }) => { + const sbPage = new SbPage(page); + + await sbPage.navigateToStory('stories/renderers/svelte/js-docs', 'docs'); + const root = sbPage.previewRoot(); + const argsTable = root.locator('.docblock-argstable'); + await expect(argsTable).toContainText('Rounds the button'); + }); + + test('TS story has auto-generated args table', async ({ page }) => { + // eslint-disable-next-line jest/valid-title + test.skip(!templateName?.endsWith('ts') || false, 'Only test TS story in TS templates'); + const sbPage = new SbPage(page); + + await sbPage.navigateToStory('stories/renderers/svelte/ts-docs', 'docs'); + const root = sbPage.previewRoot(); + const argsTable = root.locator('.docblock-argstable'); + await expect(argsTable).toContainText('Rounds the button'); + }); +}); diff --git a/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.test.ts b/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.test.ts index 45d2fb73e62f..4c8778cdc31a 100644 --- a/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.test.ts +++ b/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.test.ts @@ -18,6 +18,7 @@ const TestComponent1 = Component({})(class {}); const TestComponent2 = Component({})(class {}); const StandaloneTestComponent = Component({ standalone: true })(class {}); const TestDirective = Directive({})(class {}); +const StandaloneTestDirective = Directive({ standalone: true })(class {}); const TestModuleWithDeclarations = NgModule({ declarations: [TestComponent1] })(class {}); const TestModuleWithImportsAndProviders = NgModule({ imports: [TestModuleWithDeclarations], @@ -118,6 +119,20 @@ describe('PropertyExtractor', () => { StandaloneTestComponent, ]); }); + + it('should return standalone directives', () => { + const imports = extractImports( + { + imports: [TestModuleWithImportsAndProviders], + }, + StandaloneTestDirective + ); + expect(imports).toEqual([ + CommonModule, + TestModuleWithImportsAndProviders, + StandaloneTestDirective, + ]); + }); }); describe('extractDeclarations', () => { diff --git a/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.ts b/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.ts index d8664259e158..e6db7384488f 100644 --- a/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.ts +++ b/code/frameworks/angular/src/client/angular-beta/utils/PropertyExtractor.ts @@ -173,7 +173,7 @@ export class PropertyExtractor implements NgModuleMetadata { const isPipe = decorators.some((d) => this.isDecoratorInstanceOf(d, 'Pipe')); const isDeclarable = isComponent || isDirective || isPipe; - const isStandalone = isComponent && decorators.some((d) => d.standalone); + const isStandalone = (isComponent || isDirective) && decorators.some((d) => d.standalone); return { isDeclarable, isStandalone }; }; diff --git a/code/frameworks/nextjs/package.json b/code/frameworks/nextjs/package.json index 891ea29585ee..cbe8a52f0360 100644 --- a/code/frameworks/nextjs/package.json +++ b/code/frameworks/nextjs/package.json @@ -119,7 +119,7 @@ "@types/babel__core": "^7", "@types/babel__plugin-transform-runtime": "^7", "@types/babel__preset-env": "^7", - "next": "13.4.19", + "next": "13.5.4", "typescript": "^4.9.3", "webpack": "^5.65.0" }, diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts index f5e72bc360d8..9d7c85d8c003 100644 --- a/code/frameworks/nextjs/src/config/webpack.ts +++ b/code/frameworks/nextjs/src/config/webpack.ts @@ -48,7 +48,7 @@ const setupRuntimeConfig = (baseConfig: WebpackConfig, nextConfig: NextConfig): }), }; - const newNextLinkBehavior = nextConfig.experimental?.newNextLinkBehavior; + const newNextLinkBehavior = (nextConfig.experimental as any)?.newNextLinkBehavior; /** * In Next 13.0.0 - 13.0.5, the `newNextLinkBehavior` option now defaults to truthy (still diff --git a/code/frameworks/nextjs/src/dependency-map.ts b/code/frameworks/nextjs/src/dependency-map.ts index 70ad2ece94e0..dd848087f3e3 100644 --- a/code/frameworks/nextjs/src/dependency-map.ts +++ b/code/frameworks/nextjs/src/dependency-map.ts @@ -6,17 +6,19 @@ const mapping: Record> = { '<11.1.0': { 'next/dist/next-server/lib/router-context': 'next/dist/next-server/lib/router-context', }, - '>=11.1.0': { + '>=11.1.0 <13.5.0': { 'next/dist/shared/lib/router-context': 'next/dist/shared/lib/router-context', }, - '>=13.5.0': { - 'next/dist/shared/lib/router-context': 'next/dist/shared/lib/router-context.shared-runtime', - 'next/dist/shared/lib/head-manager-context': - 'next/dist/shared/lib/head-manager-context.shared-runtime', - 'next/dist/shared/lib/app-router-context': - 'next/dist/shared/lib/app-router-context.shared-runtime', - 'next/dist/shared/lib/hooks-client-context': - 'next/dist/shared/lib/hooks-client-context.shared-runtime', + '>=13.0.2 <13.5.0': { + 'next/dist/shared/lib/hooks-client-context.shared-runtime': + 'next/dist/shared/lib/hooks-client-context', + }, + '<13.5.0': { + 'next/dist/shared/lib/router-context.shared-runtime': 'next/dist/shared/lib/router-context', + 'next/dist/shared/lib/head-manager-context.shared-runtime': + 'next/dist/shared/lib/head-manager-context', + 'next/dist/shared/lib/app-router-context.shared-runtime': + 'next/dist/shared/lib/app-router-context', }, }; diff --git a/code/frameworks/nextjs/src/head-manager/head-manager-provider.tsx b/code/frameworks/nextjs/src/head-manager/head-manager-provider.tsx index 6d8ab263f92c..45c3a4ab4c33 100644 --- a/code/frameworks/nextjs/src/head-manager/head-manager-provider.tsx +++ b/code/frameworks/nextjs/src/head-manager/head-manager-provider.tsx @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context'; +import { HeadManagerContext } from 'next/dist/shared/lib/head-manager-context.shared-runtime'; import initHeadManager from 'next/dist/client/head-manager'; type HeadManagerValue = { diff --git a/code/frameworks/nextjs/src/routing/app-router-provider.tsx b/code/frameworks/nextjs/src/routing/app-router-provider.tsx index e25c2f1488b4..a7f0bd326f9b 100644 --- a/code/frameworks/nextjs/src/routing/app-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/app-router-provider.tsx @@ -3,11 +3,11 @@ import type { LayoutRouterContext as TLayoutRouterContext, AppRouterContext as TAppRouterContext, GlobalLayoutRouterContext as TGlobalLayoutRouterContext, -} from 'next/dist/shared/lib/app-router-context'; +} from 'next/dist/shared/lib/app-router-context.shared-runtime'; import type { PathnameContext as TPathnameContext, SearchParamsContext as TSearchParamsContext, -} from 'next/dist/shared/lib/hooks-client-context'; +} from 'next/dist/shared/lib/hooks-client-context.shared-runtime'; import type { FlightRouterState } from 'next/dist/server/app-render/types'; import type { RouteParams } from './types'; @@ -25,12 +25,16 @@ let SearchParamsContext: typeof TSearchParamsContext; let GlobalLayoutRouterContext: typeof TGlobalLayoutRouterContext; try { - AppRouterContext = require('next/dist/shared/lib/app-router-context').AppRouterContext; - LayoutRouterContext = require('next/dist/shared/lib/app-router-context').LayoutRouterContext; - PathnameContext = require('next/dist/shared/lib/hooks-client-context').PathnameContext; - SearchParamsContext = require('next/dist/shared/lib/hooks-client-context').SearchParamsContext; + AppRouterContext = + require('next/dist/shared/lib/app-router-context.shared-runtime').AppRouterContext; + LayoutRouterContext = + require('next/dist/shared/lib/app-router-context.shared-runtime').LayoutRouterContext; + PathnameContext = + require('next/dist/shared/lib/hooks-client-context.shared-runtime').PathnameContext; + SearchParamsContext = + require('next/dist/shared/lib/hooks-client-context.shared-runtime').SearchParamsContext; GlobalLayoutRouterContext = - require('next/dist/shared/lib/app-router-context').GlobalLayoutRouterContext; + require('next/dist/shared/lib/app-router-context.shared-runtime').GlobalLayoutRouterContext; } catch { AppRouterContext = React.Fragment as any; LayoutRouterContext = React.Fragment as any; diff --git a/code/frameworks/nextjs/src/routing/page-router-provider.tsx b/code/frameworks/nextjs/src/routing/page-router-provider.tsx index c882e3bee5b3..e91819fde38e 100644 --- a/code/frameworks/nextjs/src/routing/page-router-provider.tsx +++ b/code/frameworks/nextjs/src/routing/page-router-provider.tsx @@ -1,5 +1,5 @@ import type { Globals } from '@storybook/csf'; -import { RouterContext } from 'next/dist/shared/lib/router-context'; +import { RouterContext } from 'next/dist/shared/lib/router-context.shared-runtime'; import React from 'react'; import type { RouteParams } from './types'; diff --git a/code/frameworks/preact-vite/package.json b/code/frameworks/preact-vite/package.json index c33b5b9948b7..5fa528cfe7ee 100644 --- a/code/frameworks/preact-vite/package.json +++ b/code/frameworks/preact-vite/package.json @@ -58,7 +58,7 @@ }, "peerDependencies": { "preact": ">=10", - "vite": "^3.0.0 || ^4.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "engines": { "node": ">=16" diff --git a/code/frameworks/react-vite/package.json b/code/frameworks/react-vite/package.json index 71ea061a59c8..1e622335d41f 100644 --- a/code/frameworks/react-vite/package.json +++ b/code/frameworks/react-vite/package.json @@ -47,7 +47,7 @@ "prep": "../../../scripts/prepare/bundle.ts" }, "dependencies": { - "@joshwooding/vite-plugin-react-docgen-typescript": "0.2.1", + "@joshwooding/vite-plugin-react-docgen-typescript": "0.3.0", "@rollup/pluginutils": "^5.0.2", "@storybook/builder-vite": "workspace:*", "@storybook/react": "workspace:*", @@ -63,7 +63,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0", - "vite": "^3.0.0 || ^4.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "engines": { "node": ">=16" diff --git a/code/frameworks/svelte-vite/package.json b/code/frameworks/svelte-vite/package.json index 2200efa272f2..d2e3c463d689 100644 --- a/code/frameworks/svelte-vite/package.json +++ b/code/frameworks/svelte-vite/package.json @@ -52,6 +52,7 @@ "@storybook/svelte": "workspace:*", "@sveltejs/vite-plugin-svelte": "^2.4.2", "magic-string": "^0.30.0", + "svelte-preprocess": "^5.0.4", "sveltedoc-parser": "^4.2.1", "ts-dedent": "^2.2.0" }, @@ -63,7 +64,7 @@ }, "peerDependencies": { "svelte": "^3.0.0 || ^4.0.0", - "vite": "^3.0.0 || ^4.0.0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "engines": { "node": "^14.18 || >=16" diff --git a/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts b/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts index b9e8ac5fa762..bce6ab3c1a00 100644 --- a/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts +++ b/code/frameworks/svelte-vite/src/plugins/svelte-docgen.ts @@ -7,6 +7,23 @@ import type { SvelteParserOptions } from 'sveltedoc-parser'; import { logger } from '@storybook/node-logger'; import { preprocess } from 'svelte/compiler'; import { createFilter } from 'vite'; +import { replace, typescript } from 'svelte-preprocess'; + +/* + * Patch sveltedoc-parser internal options. + * Waiting for a fix for https://github.com/alexprey/sveltedoc-parser/issues/87 + */ +const svelteDocParserOptions = require('sveltedoc-parser/lib/options.js'); + +svelteDocParserOptions.getAstDefaultOptions = () => ({ + range: true, + loc: true, + comment: true, + tokens: true, + ecmaVersion: 12, + sourceType: 'module', + ecmaFeatures: {}, +}); // Most of the code here should probably be exported by @storybook/svelte and reused here. // See: https://github.com/storybookjs/storybook/blob/next/app/svelte/src/server/svelte-docgen-loader.ts @@ -48,19 +65,43 @@ export function svelteDocgen(svelteOptions: Record = {}): PluginOpt const include = /\.(svelte)$/; const filter = createFilter(include); + let docPreprocessOptions: Parameters[1] | undefined; + return { name: 'storybook:svelte-docgen-plugin', async transform(src: string, id: string) { if (!filter(id)) return undefined; + if (preprocessOptions && !docPreprocessOptions) { + /* + * We can't use vitePreprocess() for the documentation + * because it uses esbuild which removes jsdoc. + * + * By default, only typescript is transpiled, and style tags are removed. + * + * Note: these preprocessors are only used to make the component + * compatible to sveltedoc-parser (no ts), not to compile + * the component. + */ + docPreprocessOptions = [replace([[//gims, '']])]; + + try { + const ts = require.resolve('typescript'); + if (ts) { + docPreprocessOptions.unshift(typescript()); + } + } catch { + // this will error in JavaScript-only projects, this is okay + } + } + const resource = path.relative(cwd, id); let docOptions; - if (preprocessOptions) { - // eslint-disable-next-line @typescript-eslint/no-shadow - const src = fs.readFileSync(resource).toString(); + if (docPreprocessOptions) { + const rawSource = fs.readFileSync(resource).toString(); - const { code: fileContent } = await preprocess(src, preprocessOptions, { + const { code: fileContent } = await preprocess(rawSource, docPreprocessOptions, { filename: resource, }); @@ -79,21 +120,24 @@ export function svelteDocgen(svelteOptions: Record = {}): PluginOpt const s = new MagicString(src); + let componentDoc: any; try { - const componentDoc = await svelteDoc.parse(options); - // get filename for source content - const file = path.basename(resource); - - componentDoc.name = path.basename(file); - - const componentName = getNameFromFilename(resource); - s.append(`;${componentName}.__docgen = ${JSON.stringify(componentDoc)}`); + componentDoc = await svelteDoc.parse(options); } catch (error: any) { + componentDoc = { keywords: [], data: [] }; if (logDocgen) { logger.error(error); } } + // get filename for source content + const file = path.basename(resource); + + componentDoc.name = path.basename(file); + + const componentName = getNameFromFilename(resource); + s.append(`;${componentName}.__docgen = ${JSON.stringify(componentDoc)}`); + return { code: s.toString(), map: s.generateMap({ hires: true, source: id }), diff --git a/code/frameworks/svelte-vite/template/stories_svelte-vite-default-ts/ButtonTypeScript.svelte b/code/frameworks/svelte-vite/template/stories_svelte-vite-default-ts/ButtonTypeScript.svelte new file mode 100644 index 000000000000..cd00f38a3d57 --- /dev/null +++ b/code/frameworks/svelte-vite/template/stories_svelte-vite-default-ts/ButtonTypeScript.svelte @@ -0,0 +1,38 @@ + + +

Button TypeScript

+ +