Skip to content

Commit

Permalink
Merge pull request #26726 from storybookjs/valentin/add-file-search-api
Browse files Browse the repository at this point in the history
Controls: Add Channels API to search for files in the project root
  • Loading branch information
valentinpalkovic authored Apr 9, 2024
2 parents cd5d79e + 4d055fb commit 945843e
Show file tree
Hide file tree
Showing 53 changed files with 853 additions and 72 deletions.
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ jobs:
at: .
- run:
name: Install dependencies
command: yarn install
command: yarn install --no-immutable
working_directory: test-storybooks/portable-stories-kitchen-sink/<< parameters.directory >>
- run:
name: Run Jest tests
Expand Down
3 changes: 2 additions & 1 deletion code/.eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ ember-output
!.eslintrc.js
!.eslintrc-markdown.js
!.storybook
lib/core-common/templates/base-preview-head.html
lib/core-common/templates/base-preview-head.html
lib/core-server/src/utils/__search-files-tests__
7 changes: 2 additions & 5 deletions code/lib/cli/src/automigrate/helpers/mainConfigFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
rendererPackages,
frameworkPackages,
builderPackages,
extractProperFrameworkName,
} from '@storybook/core-common';
import type { StorybookConfigRaw, StorybookConfig } from '@storybook/types';
import type { ConfigFile } from '@storybook/csf-tools';
Expand All @@ -30,11 +31,7 @@ export const getFrameworkPackageName = (mainConfig?: StorybookConfigRaw) => {
return null;
}

const normalizedPath = path.normalize(packageNameOrPath).replace(new RegExp(/\\/, 'g'), '/');

return (
Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg)) || packageNameOrPath
);
return extractProperFrameworkName(packageNameOrPath);
};

/**
Expand Down
50 changes: 10 additions & 40 deletions code/lib/cli/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ import stripJsonComments from 'strip-json-comments';
import findUp from 'find-up';
import invariant from 'tiny-invariant';
import { getCliDir, getRendererDir } from './dirs';
import type {
JsPackageManager,
PackageJson,
PackageJsonWithDepsAndDevDeps,
import {
type JsPackageManager,
type PackageJson,
type PackageJsonWithDepsAndDevDeps,
frameworkToRenderer as CoreFrameworkToRenderer,
} from '@storybook/core-common';
import type { SupportedFrameworks } from '@storybook/types';
import type { SupportedRenderers } from './project_types';
import type { SupportedFrameworks, SupportedRenderers } from '@storybook/types';
import { CoreBuilder } from './project_types';
import { SupportedLanguage } from './project_types';
import { versions as storybookMonorepoPackages } from '@storybook/core-common';
Expand Down Expand Up @@ -132,40 +132,10 @@ type CopyTemplateFilesOptions = {
destination?: string;
};

export const frameworkToRenderer: Record<
SupportedFrameworks | SupportedRenderers,
SupportedRenderers | 'vue'
> = {
// frameworks
angular: 'angular',
ember: 'ember',
'html-vite': 'html',
'html-webpack5': 'html',
nextjs: 'react',
'preact-vite': 'preact',
'preact-webpack5': 'preact',
qwik: 'qwik',
'react-vite': 'react',
'react-webpack5': 'react',
'server-webpack5': 'server',
solid: 'solid',
'svelte-vite': 'svelte',
'svelte-webpack5': 'svelte',
sveltekit: 'svelte',
'vue3-vite': 'vue3',
'vue3-webpack5': 'vue3',
'web-components-vite': 'web-components',
'web-components-webpack5': 'web-components',
// renderers
html: 'html',
preact: 'preact',
'react-native': 'react-native',
react: 'react',
server: 'server',
svelte: 'svelte',
vue3: 'vue3',
'web-components': 'web-components',
};
/**
* @deprecated Please use `frameworkToRenderer` from `@storybook/core-common` instead
*/
export const frameworkToRenderer = CoreFrameworkToRenderer;

export const frameworkToDefaultBuilder: Record<SupportedFrameworks, CoreBuilder> = {
angular: CoreBuilder.Webpack5,
Expand Down
23 changes: 8 additions & 15 deletions code/lib/cli/src/project_types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { minVersion, validRange } from 'semver';
import type { SupportedFrameworks } from '@storybook/types';
import type {
SupportedFrameworks,
SupportedRenderers as CoreSupportedFrameworks,
} from '@storybook/types';

function eqMajor(versionRange: string, major: number) {
// Uses validRange to avoid a throw from minVersion if an invalid range gets passed
Expand All @@ -22,20 +25,10 @@ export const externalFrameworks: ExternalFramework[] = [
{ name: 'solid', frameworks: ['storybook-solidjs-vite'], renderer: 'storybook-solidjs' },
];

// Should match @storybook/<renderer>
export type SupportedRenderers =
| 'react'
| 'react-native'
| 'vue3'
| 'angular'
| 'ember'
| 'preact'
| 'svelte'
| 'qwik'
| 'html'
| 'web-components'
| 'server'
| 'solid';
/**
* @deprecated Please use `SupportedFrameworks` from `@storybook/types` instead
*/
export type SupportedRenderers = CoreSupportedFrameworks;

export const SUPPORTED_RENDERERS: SupportedRenderers[] = [
'react',
Expand Down
1 change: 1 addition & 0 deletions code/lib/core-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from './utils/cli';
export * from './utils/check-addon-order';
export * from './utils/envs';
export * from './utils/common-glob-options';
export * from './utils/framework-to-renderer';
export * from './utils/get-builder-options';
export * from './utils/get-framework-name';
export * from './utils/get-renderer-name';
Expand Down
36 changes: 36 additions & 0 deletions code/lib/core-common/src/utils/framework-to-renderer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { SupportedFrameworks, SupportedRenderers } from '@storybook/types';

export const frameworkToRenderer: Record<
SupportedFrameworks | SupportedRenderers,
SupportedRenderers | 'vue'
> = {
// frameworks
angular: 'angular',
ember: 'ember',
'html-vite': 'html',
'html-webpack5': 'html',
nextjs: 'react',
'preact-vite': 'preact',
'preact-webpack5': 'preact',
qwik: 'qwik',
'react-vite': 'react',
'react-webpack5': 'react',
'server-webpack5': 'server',
solid: 'solid',
'svelte-vite': 'svelte',
'svelte-webpack5': 'svelte',
sveltekit: 'svelte',
'vue3-vite': 'vue3',
'vue3-webpack5': 'vue3',
'web-components-vite': 'web-components',
'web-components-webpack5': 'web-components',
// renderers
html: 'html',
preact: 'preact',
'react-native': 'react-native',
react: 'react',
server: 'server',
svelte: 'svelte',
vue3: 'vue3',
'web-components': 'web-components',
};
18 changes: 18 additions & 0 deletions code/lib/core-common/src/utils/get-framework-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { describe, expect, it } from 'vitest';
import { extractProperFrameworkName } from './get-framework-name';

describe('get-framework-name', () => {
describe('extractProperFrameworkName', () => {
it('should extract the proper framework name from the given framework field', () => {
expect(extractProperFrameworkName('@storybook/angular')).toBe('@storybook/angular');
expect(extractProperFrameworkName('/path/to/@storybook/angular')).toBe('@storybook/angular');
expect(extractProperFrameworkName('\\path\\to\\@storybook\\angular')).toBe(
'@storybook/angular'
);
});

it('should return the given framework name if it is a third-party framework', () => {
expect(extractProperFrameworkName('@third-party/framework')).toBe('@third-party/framework');
});
});
});
16 changes: 16 additions & 0 deletions code/lib/core-common/src/utils/get-framework-name.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { dedent } from 'ts-dedent';
import type { Options } from '@storybook/types';
import { frameworkPackages } from './get-storybook-info';
import { normalizePath } from './normalize-path';

/**
* Framework can be a string or an object. This utility always returns the string name.
Expand All @@ -17,3 +19,17 @@ export async function getFrameworkName(options: Options) {

return typeof framework === 'object' ? framework.name : framework;
}

/**
* Extracts the proper framework name from the given framework field.
* The framework field can be the framework package name or a path to the framework package.
* @example
* extractProperFrameworkName('/path/to/@storybook/angular') // => '@storybook/angular'
* extractProperFrameworkName('@third-party/framework') // => '@third-party/framework'
*/
export const extractProperFrameworkName = (framework: string) => {
const normalizedPath = normalizePath(framework);
const frameworkName = Object.keys(frameworkPackages).find((pkg) => normalizedPath.endsWith(pkg));

return frameworkName ?? framework;
};
17 changes: 17 additions & 0 deletions code/lib/core-common/src/utils/get-renderer-name.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { it } from 'node:test';
import { describe, expect } from 'vitest';
import { extractProperRendererNameFromFramework } from './get-renderer-name';

describe('get-renderer-name', () => {
describe('extractProperRendererNameFromFramework', () => {
it('should return the renderer name for a known framework', async () => {
const renderer = await extractProperRendererNameFromFramework('@storybook/react');
expect(renderer).toEqual('react');
});

it('should return null for an unknown framework', async () => {
const renderer = await extractProperRendererNameFromFramework('@third-party/framework');
expect(renderer).toBeNull();
});
});
});
25 changes: 24 additions & 1 deletion code/lib/core-common/src/utils/get-renderer-name.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { Options } from '@storybook/types';
import { getFrameworkName } from './get-framework-name';
import { extractProperFrameworkName, getFrameworkName } from './get-framework-name';
import { frameworkPackages } from './get-storybook-info';
import { frameworkToRenderer } from './framework-to-renderer';

/**
* Render is set as a string on core. It must be set by the framework
* It falls back to the framework name if not set
*/
export async function getRendererName(options: Options) {
const core = await options.presets.apply('core', {}, options);
Expand All @@ -15,3 +18,23 @@ export async function getRendererName(options: Options) {

return core.renderer;
}

/**
* Extracts the proper renderer name from the given framework name.
* @param frameworkName The name of the framework.
* @returns The name of the renderer.
* @example
* extractProperRendererNameFromFramework('@storybook/react') // => 'react'
* extractProperRendererNameFromFramework('@storybook/angular') // => 'angular'
* extractProperRendererNameFromFramework('@third-party/framework') // => null
*/
export async function extractProperRendererNameFromFramework(frameworkName: string) {
const extractedFrameworkName = extractProperFrameworkName(frameworkName);
const framework = frameworkPackages[extractedFrameworkName];

if (!framework) {
return null;
}

return frameworkToRenderer[framework as keyof typeof frameworkToRenderer];
}
11 changes: 11 additions & 0 deletions code/lib/core-common/src/utils/normalize-path.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { normalizePath } from './normalize-path';
import { describe, expect, it } from 'vitest';

describe('normalize-path', () => {
it('should normalize paths', () => {
expect(normalizePath('path/to/../file')).toBe('path/file');
expect(normalizePath('path/to/./file')).toBe('path/to/file');
expect(normalizePath('path\\to\\file')).toBe('path/to/file');
expect(normalizePath('foo\\..\\bar')).toBe('bar');
});
});
14 changes: 14 additions & 0 deletions code/lib/core-common/src/utils/normalize-path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import path from 'path';

/**
* Normalize a path to use forward slashes and remove .. and .
* @param p The path to normalize
* @returns The normalized path
* @example
* normalizePath('path/to/../file') // => 'path/file'
* normalizePath('path/to/./file') // => 'path/to/file'
* normalizePath('path\\to\\file') // => 'path/to/file'
*/
export function normalizePath(p: string) {
return path.posix.normalize(p.replace(/\\/g, '/'));
}
4 changes: 4 additions & 0 deletions code/lib/core-events/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ enum events {
SET_WHATS_NEW_CACHE = 'setWhatsNewCache',
TOGGLE_WHATS_NEW_NOTIFICATIONS = 'toggleWhatsNewNotifications',
TELEMETRY_ERROR = 'telemetryError',
FILE_COMPONENT_SEARCH = 'fileComponentSearch',
FILE_COMPONENT_SEARCH_RESULT = 'fileComponentSearchResult',
}

// Enables: `import Events from ...`
Expand All @@ -87,6 +89,8 @@ export const {
CURRENT_STORY_WAS_SET,
DOCS_PREPARED,
DOCS_RENDERED,
FILE_COMPONENT_SEARCH,
FILE_COMPONENT_SEARCH_RESULT,
FORCE_RE_RENDER,
FORCE_REMOUNT,
GLOBALS_UPDATED,
Expand Down
2 changes: 2 additions & 0 deletions code/lib/core-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,11 @@
"@types/semver": "^7.3.4",
"better-opn": "^3.0.2",
"chalk": "^4.1.0",
"cjs-module-lexer": "^1.2.3",
"cli-table3": "^0.6.1",
"compression": "^1.7.4",
"detect-port": "^1.3.0",
"es-module-lexer": "^1.5.0",
"express": "^4.17.3",
"fs-extra": "^11.1.0",
"globby": "^14.0.1",
Expand Down
3 changes: 3 additions & 0 deletions code/lib/core-server/src/presets/common-preset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import invariant from 'tiny-invariant';
import { parseStaticDir } from '../utils/server-statics';
import { defaultStaticDirs } from '../utils/constants';
import { sendTelemetryError } from '../withTelemetry';
import { initFileSearchChannel } from '../server-channel/file-search-channel';

const interpolate = (string: string, data: Record<string, string> = {}) =>
Object.entries(data).reduce((acc, [k, v]) => acc.replace(new RegExp(`%${k}%`, 'g'), v), string);
Expand Down Expand Up @@ -340,6 +341,8 @@ export const experimental_serverChannel = async (
}
});

initFileSearchChannel(channel, options);

return channel;
};

Expand Down
Loading

0 comments on commit 945843e

Please sign in to comment.