-
Notifications
You must be signed in to change notification settings - Fork 47
/
Copy pathindex.ts
102 lines (90 loc) · 2.84 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { toStartCaseStr } from './toStartCaseStr';
/**
* Remove punctuation and illegal characters from a story ID.
*
* See https://gist.github.com/davidjrice/9d2af51100e41c6c4b4a
*/
export const sanitize = (string: string) => {
return (
string
.toLowerCase()
// eslint-disable-next-line no-useless-escape
.replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '-')
.replace(/-+/g, '-')
.replace(/^-+/, '')
.replace(/-+$/, '')
);
};
const sanitizeSafe = (string: string, part: string) => {
const sanitized = sanitize(string);
if (sanitized === '') {
throw new Error(`Invalid ${part} '${string}', must include alphanumeric characters`);
}
return sanitized;
};
/**
* Generate a storybook ID from a component/kind and story name.
*/
export const toId = (kind: string, name?: string) =>
`${sanitizeSafe(kind, 'kind')}${name ? `--${sanitizeSafe(name, 'name')}` : ''}`;
/**
* Transform a CSF named export into a readable story name
*/
export const storyNameFromExport = (key: string) => toStartCaseStr(key);
type StoryDescriptor = string[] | RegExp;
export interface IncludeExcludeOptions {
includeStories?: StoryDescriptor;
excludeStories?: StoryDescriptor;
}
function matches(storyKey: string, arrayOrRegex: StoryDescriptor) {
if (Array.isArray(arrayOrRegex)) {
return arrayOrRegex.includes(storyKey);
}
return storyKey.match(arrayOrRegex);
}
/**
* Does a named export match CSF inclusion/exclusion options?
*/
export function isExportStory(
key: string,
{ includeStories, excludeStories }: IncludeExcludeOptions
) {
return (
// https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs
key !== '__esModule' &&
(!includeStories || matches(key, includeStories)) &&
(!excludeStories || !matches(key, excludeStories))
);
}
export interface SeparatorOptions {
rootSeparator: string | RegExp;
groupSeparator: string | RegExp;
}
/**
* Parse out the component/kind name from a path, using the given separator config.
*/
export const parseKind = (kind: string, { rootSeparator, groupSeparator }: SeparatorOptions) => {
const [root, remainder] = kind.split(rootSeparator, 2);
const groups = (remainder || kind).split(groupSeparator).filter((i) => !!i);
// when there's no remainder, it means the root wasn't found/split
return {
root: remainder ? root : null,
groups,
};
};
/**
* Combine a set of project / meta / story tags, removing duplicates and handling negations.
*/
export const combineTags = (...tags: string[]): string[] => {
const result = tags.reduce((acc, tag) => {
if (tag.startsWith('!')) {
acc.delete(tag.slice(1));
} else {
acc.add(tag);
}
return acc;
}, new Set<string>());
return Array.from(result);
};
export { includeConditionalArg } from './includeConditionalArg';
export * from './story';