Merge branch 'next' into docs_updates_contributions

This commit is contained in:
jonniebigodes 2023-11-20 15:03:18 +00:00 committed by GitHub
commit 2f4cbb7be7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 316 additions and 114 deletions

View File

@ -62,7 +62,7 @@
"@types/cross-spawn": "^6.0.2",
"cross-spawn": "^7.0.3",
"globby": "^11.0.2",
"jscodeshift": "^0.14.0",
"jscodeshift": "^0.15.1",
"lodash": "^4.17.21",
"prettier": "^2.8.0",
"recast": "^0.23.1"

View File

@ -34,7 +34,7 @@ export const getDirectoryFromWorkingDir = ({
export const normalizeStoriesEntry = (
entry: StoriesEntry,
{ configDir, workingDir, default_files_pattern = DEFAULT_FILES_PATTERN }: NormalizeOptions
{ configDir, workingDir, defaultFilesPattern = DEFAULT_FILES_PATTERN }: NormalizeOptions
): NormalizedStoriesSpecifier => {
let specifierWithoutMatcher: Omit<NormalizedStoriesSpecifier, 'importPathMatcher'>;
@ -53,7 +53,7 @@ export const normalizeStoriesEntry = (
specifierWithoutMatcher = {
titlePrefix: DEFAULT_TITLE_PREFIX,
directory: entry,
files: default_files_pattern,
files: defaultFilesPattern,
};
} else {
specifierWithoutMatcher = {
@ -65,7 +65,7 @@ export const normalizeStoriesEntry = (
} else {
specifierWithoutMatcher = {
titlePrefix: DEFAULT_TITLE_PREFIX,
files: default_files_pattern,
files: defaultFilesPattern,
...entry,
};
}
@ -99,7 +99,7 @@ export const normalizeStoriesEntry = (
interface NormalizeOptions {
configDir: string;
workingDir: string;
default_files_pattern?: string;
defaultFilesPattern?: string;
}
export const normalizeStories = (entries: StoriesEntry[], options: NormalizeOptions) => {

View File

@ -1,14 +1,5 @@
import type {
Options,
PresetProperty,
StoriesEntry,
StorybookConfig,
TestBuildFlags,
} from '@storybook/types';
import { normalizeStories, commonGlobOptions } from '@storybook/core-common';
import { isAbsolute, join, relative } from 'path';
import slash from 'slash';
import { glob } from 'glob';
import type { Options, PresetProperty, StorybookConfig, TestBuildFlags } from '@storybook/types';
import { removeMDXEntries } from '../utils/remove-mdx-entries';
export const framework: PresetProperty<'framework', StorybookConfig> = async (config) => {
// This will get called with the values from the user's main config, but before
@ -25,49 +16,7 @@ export const framework: PresetProperty<'framework', StorybookConfig> = async (co
export const stories: PresetProperty<'stories', StorybookConfig> = async (entries, options) => {
if (options?.build?.test?.disableMDXEntries) {
const list = normalizeStories(entries, {
configDir: options.configDir,
workingDir: options.configDir,
default_files_pattern: '**/*.@(stories.@(js|jsx|mjs|ts|tsx))',
});
const result = (
await Promise.all(
list.map(async ({ directory, files, titlePrefix }) => {
const pattern = join(directory, files);
const absolutePattern = isAbsolute(pattern) ? pattern : join(options.configDir, pattern);
const absoluteDirectory = isAbsolute(directory)
? directory
: join(options.configDir, directory);
return {
files: (
await glob(slash(absolutePattern), {
...commonGlobOptions(absolutePattern),
follow: true,
})
).map((f) => relative(absoluteDirectory, f)),
directory,
titlePrefix,
};
})
)
).flatMap<StoriesEntry>((expanded, i) => {
const filteredEntries = expanded.files.filter((s) => !s.endsWith('.mdx'));
// only return the filtered entries when there is something to filter
// as webpack is faster with unexpanded globs
let items = [];
if (filteredEntries.length < expanded.files.length) {
items = filteredEntries.map((k) => ({
...expanded,
files: `**/${k}`,
}));
} else {
items = [list[i]];
}
return items;
});
return result;
return removeMDXEntries(entries, options);
}
return entries;
};

View File

@ -0,0 +1,248 @@
import { glob as globlOriginal } from 'glob';
import { type StoriesEntry } from '@storybook/types';
import { normalizeStoriesEntry } from '@storybook/core-common';
import { join } from 'path';
import slash from 'slash';
import { removeMDXEntries } from '../remove-mdx-entries';
const glob = globlOriginal as jest.MockedFunction<typeof globlOriginal>;
const configDir = '/configDir/';
const workingDir = '/';
jest.mock('glob', () => ({ glob: jest.fn() }));
const createList = (list: { entry: StoriesEntry; result: string[] }[]) => {
return list.reduce<Record<string, { result: string[]; entry: StoriesEntry }>>(
(acc, { entry, result }) => {
const { directory, files } = normalizeStoriesEntry(entry, {
configDir,
workingDir,
});
acc[slash(join('/', directory, files))] = { result, entry };
return acc;
},
{}
);
};
const createGlobMock = (input: ReturnType<typeof createList>) => {
return async (k: string | string[]) => {
if (Array.isArray(k)) {
throw new Error('do not pass an array to glob during tests');
}
if (input[slash(k)]) {
return input[slash(k)]?.result;
}
throw new Error('can not find key in input');
};
};
test('empty', async () => {
const list = createList([]);
glob.mockImplementation(createGlobMock(list));
await expect(() => removeMDXEntries(Object.keys(list), { configDir })).rejects
.toThrowErrorMatchingInlineSnapshot(`
"Storybook could not index your stories.
Your main configuration somehow does not contain a 'stories' field, or it resolved to an empty array.
Please check your main configuration file and make sure it exports a 'stories' field that is not an empty array.
More info: https://storybook.js.org/docs/react/faq#can-i-have-a-storybook-with-no-local-stories
"
`);
});
test('minimal', async () => {
const list = createList([{ entry: '*.js', result: [] }]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "*.js",
"titlePrefix": "",
},
]
`);
});
test('multiple', async () => {
const list = createList([
{ entry: '*.ts', result: [] },
{ entry: '*.js', result: [] },
]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "*.ts",
"titlePrefix": "",
},
Object {
"directory": ".",
"files": "*.js",
"titlePrefix": "",
},
]
`);
});
test('mdx but not matching any files', async () => {
const list = createList([
{ entry: '*.mdx', result: [] },
{ entry: '*.js', result: [] },
]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "*.mdx",
"titlePrefix": "",
},
Object {
"directory": ".",
"files": "*.js",
"titlePrefix": "",
},
]
`);
});
test('removes entries that only yield mdx files', async () => {
const list = createList([
{ entry: '*.mdx', result: ['/configDir/my-file.mdx'] },
{ entry: '*.js', result: [] },
]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "*.js",
"titlePrefix": "",
},
]
`);
});
test('expands entries that only yield mixed files', async () => {
const list = createList([
{ entry: '*.@(mdx|ts)', result: ['/configDir/my-file.mdx', '/configDir/my-file.ts'] },
{ entry: '*.js', result: [] },
]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "**/my-file.ts",
"titlePrefix": "",
},
Object {
"directory": ".",
"files": "*.js",
"titlePrefix": "",
},
]
`);
});
test('passes titlePrefix', async () => {
const list = createList([
{
entry: { files: '*.@(mdx|ts)', directory: '.', titlePrefix: 'foo' },
result: ['/configDir/my-file.mdx', '/configDir/my-file.ts'],
},
]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "**/my-file.ts",
"titlePrefix": "foo",
},
]
`);
});
test('expands to multiple entries', async () => {
const list = createList([
{
entry: { files: '*.@(mdx|ts)', directory: '.', titlePrefix: 'foo' },
result: [
'/configDir/my-file.mdx',
'/configDir/my-file1.ts',
'/configDir/my-file2.ts',
'/configDir/my-file3.ts',
],
},
]);
glob.mockImplementation(createGlobMock(list));
const result = await removeMDXEntries(
Object.values(list).map((e) => e.entry),
{ configDir }
);
expect(result).toMatchInlineSnapshot(`
Array [
Object {
"directory": ".",
"files": "**/my-file1.ts",
"titlePrefix": "foo",
},
Object {
"directory": ".",
"files": "**/my-file2.ts",
"titlePrefix": "foo",
},
Object {
"directory": ".",
"files": "**/my-file3.ts",
"titlePrefix": "foo",
},
]
`);
});

View File

@ -0,0 +1,57 @@
import type { Options, StoriesEntry } from '@storybook/types';
import { normalizeStories, commonGlobOptions } from '@storybook/core-common';
import { isAbsolute, join, relative } from 'path';
import slash from 'slash';
import { glob } from 'glob';
export async function removeMDXEntries(
entries: StoriesEntry[],
options: Pick<Options, 'configDir'>
): Promise<StoriesEntry[]> {
const list = normalizeStories(entries, {
configDir: options.configDir,
workingDir: options.configDir,
defaultFilesPattern: '**/*.@(stories.@(js|jsx|mjs|ts|tsx))',
});
const result = (
await Promise.all(
list.map(async ({ directory, files, titlePrefix }) => {
const pattern = join(directory, files);
const absolutePattern = isAbsolute(pattern) ? pattern : join(options.configDir, pattern);
const absoluteDirectory = isAbsolute(directory)
? directory
: join(options.configDir, directory);
return {
files: (
await glob(slash(absolutePattern), {
...commonGlobOptions(absolutePattern),
follow: true,
})
).map((f) => relative(absoluteDirectory, f)),
directory,
titlePrefix,
};
})
)
).flatMap<StoriesEntry>(({ directory, files, titlePrefix }, i) => {
const filteredEntries = files.filter((s) => !s.endsWith('.mdx'));
// only return the filtered entries when there is something to filter
// as webpack is faster with unexpanded globs
let items = [];
if (filteredEntries.length < files.length) {
items = filteredEntries.map((k) => ({
directory,
titlePrefix,
files: `**/${k}`,
}));
} else {
items = [
{ directory: list[i].directory, titlePrefix: list[i].titlePrefix, files: list[i].files },
];
}
return items;
});
return result;
}

View File

@ -47,7 +47,7 @@
"devDependencies": {
"jest": "^29.7.0",
"jest-specific-snapshot": "^8.0.0",
"jscodeshift": "^0.14.0",
"jscodeshift": "^0.15.1",
"typescript": "~4.9.3"
},
"publishConfig": {

View File

@ -6317,7 +6317,7 @@ __metadata:
globby: "npm:^11.0.2"
jest: "npm:^29.7.0"
jest-specific-snapshot: "npm:^8.0.0"
jscodeshift: "npm:^0.14.0"
jscodeshift: "npm:^0.15.1"
lodash: "npm:^4.17.21"
mdast-util-mdx-jsx: "npm:^2.1.2"
mdast-util-mdxjs-esm: "npm:^1.3.1"
@ -6922,7 +6922,7 @@ __metadata:
dependencies:
jest: "npm:^29.7.0"
jest-specific-snapshot: "npm:^8.0.0"
jscodeshift: "npm:^0.14.0"
jscodeshift: "npm:^0.15.1"
typescript: "npm:~4.9.3"
languageName: unknown
linkType: soft
@ -11154,15 +11154,6 @@ __metadata:
languageName: node
linkType: hard
"ast-types@npm:0.15.2":
version: 0.15.2
resolution: "ast-types@npm:0.15.2"
dependencies:
tslib: "npm:^2.0.1"
checksum: 5b26e3656e9e8d1db8c8d14971d0cb88ca0138aacce72171cb4cd4555fc8dc53c07e821c568e57fe147366931708fefd25cb9d7e880d42ce9cb569947844c962
languageName: node
linkType: hard
"ast-types@npm:^0.16.1":
version: 0.16.1
resolution: "ast-types@npm:0.16.1"
@ -20127,37 +20118,6 @@ __metadata:
languageName: node
linkType: hard
"jscodeshift@npm:^0.14.0":
version: 0.14.0
resolution: "jscodeshift@npm:0.14.0"
dependencies:
"@babel/core": "npm:^7.13.16"
"@babel/parser": "npm:^7.13.16"
"@babel/plugin-proposal-class-properties": "npm:^7.13.0"
"@babel/plugin-proposal-nullish-coalescing-operator": "npm:^7.13.8"
"@babel/plugin-proposal-optional-chaining": "npm:^7.13.12"
"@babel/plugin-transform-modules-commonjs": "npm:^7.13.8"
"@babel/preset-flow": "npm:^7.13.13"
"@babel/preset-typescript": "npm:^7.13.0"
"@babel/register": "npm:^7.13.16"
babel-core: "npm:^7.0.0-bridge.0"
chalk: "npm:^4.1.2"
flow-parser: "npm:0.*"
graceful-fs: "npm:^4.2.4"
micromatch: "npm:^4.0.4"
neo-async: "npm:^2.5.0"
node-dir: "npm:^0.1.17"
recast: "npm:^0.21.0"
temp: "npm:^0.8.4"
write-file-atomic: "npm:^2.3.0"
peerDependencies:
"@babel/preset-env": ^7.1.6
bin:
jscodeshift: bin/jscodeshift.js
checksum: dab63bdb4b7e67d79634fcd3f5dc8b227146e9f68aa88700bc49c5a45b6339d05bd934a98aa53d29abd04f81237d010e7e037799471b2aab66ec7b9a7d752786
languageName: node
linkType: hard
"jscodeshift@npm:^0.15.1":
version: 0.15.1
resolution: "jscodeshift@npm:0.15.1"
@ -25954,18 +25914,6 @@ __metadata:
languageName: node
linkType: hard
"recast@npm:^0.21.0":
version: 0.21.5
resolution: "recast@npm:0.21.5"
dependencies:
ast-types: "npm:0.15.2"
esprima: "npm:~4.0.0"
source-map: "npm:~0.6.1"
tslib: "npm:^2.0.1"
checksum: a45168c82195f24fa2c70293a624fece0069a2e8e8adb637f9963777735f81cb3bb62e55172db677ec3573b08b2daaf1eddd85b74da6fe0bd37c9b15eeaf94b4
languageName: node
linkType: hard
"recast@npm:^0.23.1, recast@npm:^0.23.3":
version: 0.23.4
resolution: "recast@npm:0.23.4"