@storybook/csf 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- package/.babelrc.js +6 -0
- package/README.md +33 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +105 -0
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +137 -0
- package/package.json +65 -0
package/.babelrc.js
ADDED
package/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Storybook Component Story Format (CSF)
|
2
|
+
|
3
|
+
A minimal set of utility functions for dealing with Storybook [Component Story Format (CSF)](https://storybook.js.org/docs/formats/component-story-format/).
|
4
|
+
|
5
|
+
## Install
|
6
|
+
|
7
|
+
```sh
|
8
|
+
yarn add @storybook/csf
|
9
|
+
```
|
10
|
+
|
11
|
+
## API
|
12
|
+
|
13
|
+
See package source for function definitions and types:
|
14
|
+
|
15
|
+
- `isExportStory(key, { includeStories, excludeStories })` - Does a named export match CSF inclusion/exclusion options?
|
16
|
+
|
17
|
+
- `parseKind(kind, { rootSeparator, groupSeparator })` - Parse out the component/kind name from a path, using the given separator config.
|
18
|
+
|
19
|
+
- `sanitize(string)` - Remove punctuation and illegal characters from a story ID.
|
20
|
+
|
21
|
+
- `toId(kind, name)` - Generate a storybook ID from a component/kind and story name.
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
If you have any suggestions, please open an issue or a PR.
|
26
|
+
|
27
|
+
All contributions are welcome!
|
28
|
+
|
29
|
+
### run tests:
|
30
|
+
|
31
|
+
```sh
|
32
|
+
yarn test
|
33
|
+
```
|
package/dist/index.d.ts
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
export declare const sanitize: (string: string) => string;
|
2
|
+
export declare const toId: (kind: string, name: string) => string;
|
3
|
+
export declare const storyNameFromExport: (key: string) => string;
|
4
|
+
declare type StoryDescriptor = string[] | RegExp;
|
5
|
+
export interface IncludeExcludeOptions {
|
6
|
+
includeStories?: StoryDescriptor;
|
7
|
+
excludeStories?: StoryDescriptor;
|
8
|
+
}
|
9
|
+
export declare function isExportStory(key: string, { includeStories, excludeStories }: IncludeExcludeOptions): boolean | null;
|
10
|
+
export interface SeparatorOptions {
|
11
|
+
rootSeparator: string | RegExp;
|
12
|
+
groupSeparator: string | RegExp;
|
13
|
+
}
|
14
|
+
export declare const parseKind: (kind: string, { rootSeparator, groupSeparator }: SeparatorOptions) => {
|
15
|
+
root: string | null;
|
16
|
+
groups: string[];
|
17
|
+
};
|
18
|
+
export {};
|
package/dist/index.js
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
4
|
+
value: true
|
5
|
+
});
|
6
|
+
exports.isExportStory = isExportStory;
|
7
|
+
exports.parseKind = exports.storyNameFromExport = exports.toId = exports.sanitize = void 0;
|
8
|
+
|
9
|
+
var _startCase = _interopRequireDefault(require("lodash/startCase"));
|
10
|
+
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
|
12
|
+
|
13
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
|
14
|
+
|
15
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
|
16
|
+
|
17
|
+
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
18
|
+
|
19
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
20
|
+
|
21
|
+
/**
|
22
|
+
* Remove punctuation and illegal characters from a story ID.
|
23
|
+
*
|
24
|
+
* See https://gist.github.com/davidjrice/9d2af51100e41c6c4b4a
|
25
|
+
*/
|
26
|
+
var sanitize = function sanitize(string) {
|
27
|
+
return string.toLowerCase() // eslint-disable-next-line no-useless-escape
|
28
|
+
.replace(/[ ’–—―′¿'`~!@#$%^&*()_|+\-=?;:'",.<>\{\}\[\]\\\/]/gi, '-').replace(/-+/g, '-').replace(/^-+/, '').replace(/-+$/, '');
|
29
|
+
};
|
30
|
+
|
31
|
+
exports.sanitize = sanitize;
|
32
|
+
|
33
|
+
var sanitizeSafe = function sanitizeSafe(string, part) {
|
34
|
+
var sanitized = sanitize(string);
|
35
|
+
|
36
|
+
if (sanitized === '') {
|
37
|
+
throw new Error("Invalid ".concat(part, " '").concat(string, "', must include alphanumeric characters"));
|
38
|
+
}
|
39
|
+
|
40
|
+
return sanitized;
|
41
|
+
};
|
42
|
+
/**
|
43
|
+
* Generate a storybook ID from a component/kind and story name.
|
44
|
+
*/
|
45
|
+
|
46
|
+
|
47
|
+
var toId = function toId(kind, name) {
|
48
|
+
return "".concat(sanitizeSafe(kind, 'kind'), "--").concat(sanitizeSafe(name, 'name'));
|
49
|
+
};
|
50
|
+
/**
|
51
|
+
* Transform a CSF named export into a readable story name
|
52
|
+
*/
|
53
|
+
|
54
|
+
|
55
|
+
exports.toId = toId;
|
56
|
+
|
57
|
+
var storyNameFromExport = function storyNameFromExport(key) {
|
58
|
+
return (0, _startCase["default"])(key);
|
59
|
+
};
|
60
|
+
|
61
|
+
exports.storyNameFromExport = storyNameFromExport;
|
62
|
+
|
63
|
+
function matches(storyKey, arrayOrRegex) {
|
64
|
+
if (Array.isArray(arrayOrRegex)) {
|
65
|
+
return arrayOrRegex.includes(storyKey);
|
66
|
+
}
|
67
|
+
|
68
|
+
return storyKey.match(arrayOrRegex);
|
69
|
+
}
|
70
|
+
/**
|
71
|
+
* Does a named export match CSF inclusion/exclusion options?
|
72
|
+
*/
|
73
|
+
|
74
|
+
|
75
|
+
function isExportStory(key, _ref) {
|
76
|
+
var includeStories = _ref.includeStories,
|
77
|
+
excludeStories = _ref.excludeStories;
|
78
|
+
return (// https://babeljs.io/docs/en/babel-plugin-transform-modules-commonjs
|
79
|
+
key !== '__esModule' && (!includeStories || matches(key, includeStories)) && (!excludeStories || !matches(key, excludeStories))
|
80
|
+
);
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* Parse out the component/kind name from a path, using the given separator config.
|
85
|
+
*/
|
86
|
+
var parseKind = function parseKind(kind, _ref2) {
|
87
|
+
var rootSeparator = _ref2.rootSeparator,
|
88
|
+
groupSeparator = _ref2.groupSeparator;
|
89
|
+
|
90
|
+
var _kind$split = kind.split(rootSeparator, 2),
|
91
|
+
_kind$split2 = _slicedToArray(_kind$split, 2),
|
92
|
+
root = _kind$split2[0],
|
93
|
+
remainder = _kind$split2[1];
|
94
|
+
|
95
|
+
var groups = (remainder || kind).split(groupSeparator).filter(function (i) {
|
96
|
+
return !!i;
|
97
|
+
}); // when there's no remainder, it means the root wasn't found/split
|
98
|
+
|
99
|
+
return {
|
100
|
+
root: remainder ? root : null,
|
101
|
+
groups: groups
|
102
|
+
};
|
103
|
+
};
|
104
|
+
|
105
|
+
exports.parseKind = parseKind;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,137 @@
|
|
1
|
+
"use strict";
|
2
|
+
|
3
|
+
var _ = require(".");
|
4
|
+
|
5
|
+
function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
|
6
|
+
|
7
|
+
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
|
8
|
+
|
9
|
+
function _iterableToArrayLimit(arr, i) { if (!(Symbol.iterator in Object(arr) || Object.prototype.toString.call(arr) === "[object Arguments]")) { return; } var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
|
10
|
+
|
11
|
+
function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
|
12
|
+
|
13
|
+
describe('toId', function () {
|
14
|
+
[// name, kind, story, output
|
15
|
+
['handles simple cases', 'kind', 'story', 'kind--story'], ['handles basic substitution', 'a b$c?d😀e', '1-2:3', 'a-b-c-d😀e--1-2-3'], ['handles runs of non-url chars', 'a?&*b', 'story', 'a-b--story'], ['removes non-url chars from start and end', '?ab-', 'story', 'ab--story'], ['downcases', 'KIND', 'STORY', 'kind--story'], ['non-latin', 'Кнопки', 'нормальный', 'кнопки--нормальный'], ['korean', 'kind', '바보 (babo)', 'kind--바보-babo'], ['all punctuation', 'kind', 'unicorns,’–—―′¿`"<>()!.!!!{}[]%^&$*#&', 'kind--unicorns']].forEach(function (_ref) {
|
16
|
+
var _ref2 = _slicedToArray(_ref, 4),
|
17
|
+
name = _ref2[0],
|
18
|
+
kind = _ref2[1],
|
19
|
+
story = _ref2[2],
|
20
|
+
output = _ref2[3];
|
21
|
+
|
22
|
+
it(name, function () {
|
23
|
+
expect((0, _.toId)(kind, story)).toBe(output);
|
24
|
+
});
|
25
|
+
});
|
26
|
+
it('does not allow kind with *no* url chars', function () {
|
27
|
+
expect(function () {
|
28
|
+
return (0, _.toId)('?', 'asdf');
|
29
|
+
}).toThrow("Invalid kind '?', must include alphanumeric characters");
|
30
|
+
});
|
31
|
+
it('does not allow empty kind', function () {
|
32
|
+
expect(function () {
|
33
|
+
return (0, _.toId)('', 'asdf');
|
34
|
+
}).toThrow("Invalid kind '', must include alphanumeric characters");
|
35
|
+
});
|
36
|
+
it('does not allow story with *no* url chars', function () {
|
37
|
+
expect(function () {
|
38
|
+
return (0, _.toId)('kind', '?');
|
39
|
+
}).toThrow("Invalid name '?', must include alphanumeric characters");
|
40
|
+
});
|
41
|
+
it('does not allow empty story', function () {
|
42
|
+
expect(function () {
|
43
|
+
return (0, _.toId)('kind', '');
|
44
|
+
}).toThrow("Invalid name '', must include alphanumeric characters");
|
45
|
+
});
|
46
|
+
});
|
47
|
+
describe('storyNameFromExport', function () {
|
48
|
+
it('should format CSF exports with sensible defaults', function () {
|
49
|
+
var testCases = {
|
50
|
+
name: 'Name',
|
51
|
+
someName: 'Some Name',
|
52
|
+
someNAME: 'Some NAME',
|
53
|
+
some_custom_NAME: 'Some Custom NAME',
|
54
|
+
someName1234: 'Some Name 1234',
|
55
|
+
someName1_2_3_4: 'Some Name 1 2 3 4'
|
56
|
+
};
|
57
|
+
Object.entries(testCases).forEach(function (_ref3) {
|
58
|
+
var _ref4 = _slicedToArray(_ref3, 2),
|
59
|
+
key = _ref4[0],
|
60
|
+
val = _ref4[1];
|
61
|
+
|
62
|
+
return expect((0, _.storyNameFromExport)(key)).toBe(val);
|
63
|
+
});
|
64
|
+
});
|
65
|
+
});
|
66
|
+
describe('isExportStory', function () {
|
67
|
+
it('should exclude __esModule', function () {
|
68
|
+
expect((0, _.isExportStory)('__esModule', {})).toBeFalsy();
|
69
|
+
});
|
70
|
+
it('should include all stories when there are no filters', function () {
|
71
|
+
expect((0, _.isExportStory)('a', {})).toBeTruthy();
|
72
|
+
});
|
73
|
+
it('should filter stories by arrays', function () {
|
74
|
+
expect((0, _.isExportStory)('a', {
|
75
|
+
includeStories: ['a']
|
76
|
+
})).toBeTruthy();
|
77
|
+
expect((0, _.isExportStory)('a', {
|
78
|
+
includeStories: []
|
79
|
+
})).toBeFalsy();
|
80
|
+
expect((0, _.isExportStory)('a', {
|
81
|
+
includeStories: ['b']
|
82
|
+
})).toBeFalsy();
|
83
|
+
expect((0, _.isExportStory)('a', {
|
84
|
+
excludeStories: ['a']
|
85
|
+
})).toBeFalsy();
|
86
|
+
expect((0, _.isExportStory)('a', {
|
87
|
+
excludeStories: []
|
88
|
+
})).toBeTruthy();
|
89
|
+
expect((0, _.isExportStory)('a', {
|
90
|
+
excludeStories: ['b']
|
91
|
+
})).toBeTruthy();
|
92
|
+
expect((0, _.isExportStory)('a', {
|
93
|
+
includeStories: ['a'],
|
94
|
+
excludeStories: ['a']
|
95
|
+
})).toBeFalsy();
|
96
|
+
expect((0, _.isExportStory)('a', {
|
97
|
+
includeStories: [],
|
98
|
+
excludeStories: []
|
99
|
+
})).toBeFalsy();
|
100
|
+
expect((0, _.isExportStory)('a', {
|
101
|
+
includeStories: ['a'],
|
102
|
+
excludeStories: ['b']
|
103
|
+
})).toBeTruthy();
|
104
|
+
});
|
105
|
+
it('should filter stories by regex', function () {
|
106
|
+
expect((0, _.isExportStory)('a', {
|
107
|
+
includeStories: /a/
|
108
|
+
})).toBeTruthy();
|
109
|
+
expect((0, _.isExportStory)('a', {
|
110
|
+
includeStories: /.*/
|
111
|
+
})).toBeTruthy();
|
112
|
+
expect((0, _.isExportStory)('a', {
|
113
|
+
includeStories: /b/
|
114
|
+
})).toBeFalsy();
|
115
|
+
expect((0, _.isExportStory)('a', {
|
116
|
+
excludeStories: /a/
|
117
|
+
})).toBeFalsy();
|
118
|
+
expect((0, _.isExportStory)('a', {
|
119
|
+
excludeStories: /.*/
|
120
|
+
})).toBeFalsy();
|
121
|
+
expect((0, _.isExportStory)('a', {
|
122
|
+
excludeStories: /b/
|
123
|
+
})).toBeTruthy();
|
124
|
+
expect((0, _.isExportStory)('a', {
|
125
|
+
includeStories: /a/,
|
126
|
+
excludeStories: ['a']
|
127
|
+
})).toBeFalsy();
|
128
|
+
expect((0, _.isExportStory)('a', {
|
129
|
+
includeStories: /.*/,
|
130
|
+
excludeStories: /.*/
|
131
|
+
})).toBeFalsy();
|
132
|
+
expect((0, _.isExportStory)('a', {
|
133
|
+
includeStories: /a/,
|
134
|
+
excludeStories: /b/
|
135
|
+
})).toBeTruthy();
|
136
|
+
});
|
137
|
+
});
|
package/package.json
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
{
|
2
|
+
"name": "@storybook/csf",
|
3
|
+
"version": "0.0.1",
|
4
|
+
"description": "Storybook Component Story Format (CSF) utilities",
|
5
|
+
"keywords": [
|
6
|
+
"storybook",
|
7
|
+
"component story format",
|
8
|
+
"csf",
|
9
|
+
"stories"
|
10
|
+
],
|
11
|
+
"homepage": "https://github.com/storybookjs/csf",
|
12
|
+
"bugs": {
|
13
|
+
"url": "https://github.com/storybookjs/csf/issues"
|
14
|
+
},
|
15
|
+
"repository": {
|
16
|
+
"type": "git",
|
17
|
+
"url": "https://github.com/storybookjs/csf.git"
|
18
|
+
},
|
19
|
+
"license": "MIT",
|
20
|
+
"files": [
|
21
|
+
"dist/**/*",
|
22
|
+
"README.md",
|
23
|
+
"*.js",
|
24
|
+
"*.d.ts"
|
25
|
+
],
|
26
|
+
"main": "dist/index.js",
|
27
|
+
"types": "dist/index.d.ts",
|
28
|
+
"scripts": {
|
29
|
+
"build": "babel src --out-dir dist --extensions \".ts\" && tsc --emitDeclarationOnly",
|
30
|
+
"lint": "eslint src --ext .js,.ts",
|
31
|
+
"prepublish": "yarn build",
|
32
|
+
"test": "jest"
|
33
|
+
},
|
34
|
+
"eslintConfig": {
|
35
|
+
"extends": [
|
36
|
+
"@storybook/eslint-config-storybook"
|
37
|
+
]
|
38
|
+
},
|
39
|
+
"prettier": "@storybook/linter-config/prettier.config",
|
40
|
+
"jest": {
|
41
|
+
"testEnvironment": "node"
|
42
|
+
},
|
43
|
+
"dependencies": {
|
44
|
+
"lodash": "^4.17.15"
|
45
|
+
},
|
46
|
+
"devDependencies": {
|
47
|
+
"@babel/cli": "^7.7.4",
|
48
|
+
"@babel/core": "^7.7.4",
|
49
|
+
"@babel/preset-env": "^7.7.4",
|
50
|
+
"@babel/preset-typescript": "^7.7.4",
|
51
|
+
"@storybook/eslint-config-storybook": "^2.1.0",
|
52
|
+
"@types/jest": "^24.0.23",
|
53
|
+
"@types/lodash": "^4.14.149",
|
54
|
+
"babel-core": "7.0.0-bridge.0",
|
55
|
+
"babel-jest": "^24.9.0",
|
56
|
+
"common-tags": "^1.8.0",
|
57
|
+
"eslint": "^6.7.1",
|
58
|
+
"jest": "^24.9.0",
|
59
|
+
"prettier": "^1.19.1",
|
60
|
+
"typescript": "^3.7.2"
|
61
|
+
},
|
62
|
+
"publishConfig": {
|
63
|
+
"access": "public"
|
64
|
+
}
|
65
|
+
}
|