@vonage/vivid-react 0.0.1 → 2.10.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/main.js +11 -0
- package/.storybook/preview.js +3 -0
- package/README.md +183 -0
- package/babel.config.js +38 -0
- package/package.json +135 -7
- package/rollup.config.js +30 -0
- package/scripts/cli.js +28 -0
- package/scripts/generateWrappers/consts.js +226 -0
- package/scripts/generateWrappers/generator.js +130 -0
- package/scripts/generateWrappers/helpers/generator.js +18 -0
- package/scripts/generateWrappers/helpers/generator.test.js +56 -0
- package/scripts/generateWrappers/index.js +0 -0
- package/scripts/generateWrappers/prop.types.js +86 -0
- package/scripts/generateWrappers/templates/js/index.tmpl +2 -0
- package/scripts/generateWrappers/templates/js/react-component.tmpl +30 -0
- package/scripts/generateWrappers/templates/js/stories.tmpl +18 -0
- package/scripts/generateWrappers/templates/templates.js +36 -0
- package/scripts/generateWrappers/templates/ts/index.tmpl +2 -0
- package/scripts/generateWrappers/templates/ts/react-component.tmpl +28 -0
- package/scripts/generateWrappers/utils.js +226 -0
- package/scripts/generateWrappers/utils.test.js +71 -0
- package/scripts/updateDocs.js +42 -0
- package/scripts/updateVividPackages.js +81 -0
- package/src/dataGrid/index.js +15 -0
- package/src/index.js +3 -0
- package/src/initialization/initVivid.js +24 -0
- package/src/patches/VwcCard/index.js +2 -0
- package/src/patches/VwcCard/vwcCardHeadingCrop.js +28 -0
- package/src/patches/VwcCard/vwcCardWithMenu.js +14 -0
- package/src/patches/index.js +6 -0
- package/src/patches/vwcDataGrid.js +26 -0
- package/src/patches/vwcNoteContentOverflow.js +20 -0
- package/src/patches/vwcSetAttributeValue.js +12 -0
- package/src/patches/vwcSnackBarHtmlMessage.js +21 -0
- package/src/patches/vwcTooltip.js +46 -0
- package/stories/VwcAccordion/VwcAccordion.stories.jsx +18 -0
- package/stories/VwcActionGroup/VwcActionGroup.stories.jsx +18 -0
- package/stories/VwcBadge/VwcBadge.stories.jsx +18 -0
- package/stories/VwcBanner/VwcBanner.stories.jsx +18 -0
- package/stories/VwcButton/VwcButton.stories.jsx +18 -0
- package/stories/VwcButtonToggleGroup/VwcButtonToggleGroup.stories.jsx +47 -0
- package/stories/VwcCalendar/VwcCalendar.stories.jsx +18 -0
- package/stories/VwcCalendarEvent/VwcCalendarEvent.stories.jsx +18 -0
- package/stories/VwcCard/VwcCard.stories.jsx +90 -0
- package/stories/VwcCarousel/VwcCarousel.stories.jsx +18 -0
- package/stories/VwcCarouselItem/VwcCarouselItem.stories.jsx +18 -0
- package/stories/VwcCheckListItem/VwcCheckListItem.stories.jsx +18 -0
- package/stories/VwcCheckbox/VwcCheckbox.stories.jsx +18 -0
- package/stories/VwcChip/VwcChip.stories.jsx +18 -0
- package/stories/VwcChipSet/VwcChipSet.stories.jsx +38 -0
- package/stories/VwcCircularProgress/VwcCircularProgress.stories.jsx +18 -0
- package/stories/VwcDataGrid/VwcDataGrid.stories.jsx +109 -0
- package/stories/VwcDataGridColumn/VwcDataGridColumn.stories.jsx +18 -0
- package/stories/VwcDatepicker/VwcDatepicker.stories.jsx +18 -0
- package/stories/VwcDialog/VwcDialog.stories.jsx +18 -0
- package/stories/VwcDrawer/VwcDrawer.stories.jsx +18 -0
- package/stories/VwcDropdown/VwcDropdown.stories.jsx +18 -0
- package/stories/VwcElevation/VwcElevation.stories.jsx +18 -0
- package/stories/VwcEmptyState/VwcEmptyState.stories.jsx +18 -0
- package/stories/VwcExpansionPanel/VwcExpansionPanel.stories.jsx +18 -0
- package/stories/VwcFab/VwcFab.stories.jsx +18 -0
- package/stories/VwcFilePicker/VwcFilePicker.stories.jsx +18 -0
- package/stories/VwcFormfield/VwcFormfield.stories.jsx +18 -0
- package/stories/VwcHelperMessage/VwcHelperMessage.stories.jsx +18 -0
- package/stories/VwcIcon/VwcIcon.stories.jsx +18 -0
- package/stories/VwcIconButton/VwcIconButton.stories.jsx +18 -0
- package/stories/VwcIconButtonToggle/VwcIconButtonToggle.stories.jsx +18 -0
- package/stories/VwcKeypad/VwcKeypad.stories.jsx +18 -0
- package/stories/VwcLayout/VwcLayout.stories.jsx +63 -0
- package/stories/VwcLinearProgress/VwcLinearProgress.stories.jsx +18 -0
- package/stories/VwcList/VwcList.stories.jsx +25 -0
- package/stories/VwcListExpansionPanel/VwcListExpansionPanel.stories.jsx +18 -0
- package/stories/VwcListItem/VwcListItem.stories.jsx +18 -0
- package/stories/VwcMenu/VwcMenu.stories.jsx +41 -0
- package/stories/VwcNotchedOutline/VwcNotchedOutline.stories.jsx +18 -0
- package/stories/VwcNote/VwcNote.stories.jsx +18 -0
- package/stories/VwcPagination/VwcPagination.stories.jsx +18 -0
- package/stories/VwcPopup/VwcPopup.stories.jsx +18 -0
- package/stories/VwcRadio/VwcRadio.stories.jsx +18 -0
- package/stories/VwcRadioListItem/VwcRadioListItem.stories.jsx +18 -0
- package/stories/VwcSchemeSelect/VwcSchemeSelect.stories.jsx +18 -0
- package/stories/VwcSelect/VwcSelect.stories.jsx +18 -0
- package/stories/VwcSideDrawer/VwcSideDrawer.stories.jsx +18 -0
- package/stories/VwcSlider/VwcSlider.stories.jsx +18 -0
- package/stories/VwcSnackbar/VwcSnackbar.stories.jsx +18 -0
- package/stories/VwcSurface/VwcSurface.stories.jsx +18 -0
- package/stories/VwcSwitch/VwcSwitch.stories.jsx +18 -0
- package/stories/VwcTab/VwcTab.stories.jsx +18 -0
- package/stories/VwcTabBar/VwcTabBar.stories.jsx +63 -0
- package/stories/VwcTag/VwcTag.stories.jsx +18 -0
- package/stories/VwcText/VwcText.stories.jsx +18 -0
- package/stories/VwcTextarea/VwcTextarea.stories.jsx +24 -0
- package/stories/VwcTextfield/VwcTextfield.stories.jsx +47 -0
- package/stories/VwcThemeSwitch/VwcThemeSwitch.stories.jsx +18 -0
- package/stories/VwcTooltip/VwcTooltip.stories.jsx +41 -0
- package/stories/VwcTopAppBar/VwcTopAppBar.stories.jsx +18 -0
- package/stories/VwcTopAppBarFixed/VwcTopAppBarFixed.stories.jsx +18 -0
- package/stories/patches/patches.stories.jsx +94 -0
- package/testing/component.mock.js +3 -0
- package/tsconfig.json +70 -0
- package/types/index.d.ts +2 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
const packageJson = require('../../package.json')
|
|
2
|
+
const { getImportsFromTag } = require('./helpers/generator')
|
|
3
|
+
const {
|
|
4
|
+
ComponentsEventsMap,
|
|
5
|
+
CompoundComponentsMap,
|
|
6
|
+
OutputLanguage,
|
|
7
|
+
FileName
|
|
8
|
+
} = require('./consts')
|
|
9
|
+
|
|
10
|
+
const { pathExists, outputFile, outputJson } = require('fs-extra')
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
compileTypescript,
|
|
14
|
+
prepareDir,
|
|
15
|
+
getComponentName,
|
|
16
|
+
toJsonObjectsList,
|
|
17
|
+
filePath,
|
|
18
|
+
renderJsDoc,
|
|
19
|
+
getIndexFileName,
|
|
20
|
+
getProperties,
|
|
21
|
+
getUniqueEvents,
|
|
22
|
+
getVividPackageName,
|
|
23
|
+
prepareCompoundComponents,
|
|
24
|
+
compoundComponentTemplate
|
|
25
|
+
} = require('./utils')
|
|
26
|
+
const { getTemplate, TemplateToken } = require('./templates/templates')
|
|
27
|
+
const { join } = require('path')
|
|
28
|
+
const { getPropTypes, getDefaultProps, getProps } = require('./prop.types')
|
|
29
|
+
|
|
30
|
+
const generateTypings = outputDir => async tags => {
|
|
31
|
+
const distTs = join(FileName.tempFolder, FileName.tempTsFolder)
|
|
32
|
+
await generateWrappers(distTs, OutputLanguage.TypeScript, false, false)(tags)
|
|
33
|
+
await compileTypescript(distTs)(outputDir)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const renderComponent = tag => language => componentName => {
|
|
37
|
+
const compoundsConfig = CompoundComponentsMap[componentName] || {}
|
|
38
|
+
const getCompoundComponents = prepareCompoundComponents(componentName, compoundComponentTemplate, compoundsConfig)
|
|
39
|
+
return getTemplate('react-component', language)
|
|
40
|
+
.replace(TemplateToken.CLASS_JSDOC, renderJsDoc(tag))
|
|
41
|
+
.replace(TemplateToken.IMPORTS, getImportsFromTag(tag).join('\n'))
|
|
42
|
+
.replace(TemplateToken.EVENTS, toJsonObjectsList(getUniqueEvents(tag.events)))
|
|
43
|
+
.replace(TemplateToken.PROPERTIES,
|
|
44
|
+
toJsonObjectsList(
|
|
45
|
+
getProperties(tag)
|
|
46
|
+
.filter(property => property.bindable)
|
|
47
|
+
.map(({ name }) => name))
|
|
48
|
+
)
|
|
49
|
+
.replace(TemplateToken.ATTRIBUTES, '')
|
|
50
|
+
.replace(TemplateToken.PROP_TYPES, getPropTypes(tag).join(',\n'))
|
|
51
|
+
.replace(TemplateToken.PROPS, getProps(tag).join(',\n'))
|
|
52
|
+
.replace(TemplateToken.DEFAULT_PROPS, getDefaultProps(tag).join(',\n'))
|
|
53
|
+
.replace(TemplateToken.TAG_DESCRIPTOR_JSON, JSON.stringify(tag, null, ' '))
|
|
54
|
+
.replace(TemplateToken.COMPOUND_COMPONENTS, getCompoundComponents())
|
|
55
|
+
.replace(TemplateToken.REACT_IMPORT, getCompoundComponents() &&
|
|
56
|
+
'import { createElement } from \'react\'')
|
|
57
|
+
.replace(new RegExp(TemplateToken.COMPONENT_CLASS_NAME, 'g'), componentName)
|
|
58
|
+
.replace(new RegExp(TemplateToken.TAG, 'g'), tag.name)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const generateWrappers = (outputDir, language = OutputLanguage.JavaScript, cleanTemp = true, verbose = true) => async (tags) => {
|
|
62
|
+
const indexFileName = getIndexFileName(language)
|
|
63
|
+
const saveIndex = (outputDir, content) => {
|
|
64
|
+
const indexOutputFileName = join(outputDir, indexFileName)
|
|
65
|
+
return outputFile(
|
|
66
|
+
indexOutputFileName,
|
|
67
|
+
content
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const saveStory = async (outputDir, componentName, content) => {
|
|
72
|
+
const indexOutputFileName = join(outputDir, `${componentName}.stories.jsx`)
|
|
73
|
+
const exists = await pathExists(indexOutputFileName)
|
|
74
|
+
return exists || outputFile(
|
|
75
|
+
indexOutputFileName,
|
|
76
|
+
content
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const getStoriesContent = (componentName, tag) =>
|
|
81
|
+
getTemplate('stories', 'js')
|
|
82
|
+
.split(TemplateToken.COMPONENT_CLASS_NAME).join(componentName)
|
|
83
|
+
|
|
84
|
+
prepareDir(outputDir, true, verbose)
|
|
85
|
+
const components = []
|
|
86
|
+
|
|
87
|
+
for (const tag of tags) {
|
|
88
|
+
const componentName = getComponentName(tag)
|
|
89
|
+
components.push(componentName)
|
|
90
|
+
if (verbose) {
|
|
91
|
+
console.info(`Processing ${componentName}...`)
|
|
92
|
+
}
|
|
93
|
+
tag.events = [...(tag.events || []), ...(ComponentsEventsMap[componentName] || [])]
|
|
94
|
+
|
|
95
|
+
const componentOutputDir = join(process.cwd(), outputDir, componentName)
|
|
96
|
+
const storyOutputDir = join(process.cwd(), FileName.storyOutputDir, componentName)
|
|
97
|
+
const componentContent = renderComponent(tag)(language)(componentName)
|
|
98
|
+
|
|
99
|
+
await saveIndex(componentOutputDir, componentContent)
|
|
100
|
+
await saveStory(storyOutputDir, componentName, getStoriesContent(componentName, tag))
|
|
101
|
+
|
|
102
|
+
const packageName = getVividPackageName(tag.path)
|
|
103
|
+
const packageJsonContent = {
|
|
104
|
+
name: `@vonage/vivid-react-${tag.name}`,
|
|
105
|
+
version: packageJson.version,
|
|
106
|
+
main: indexFileName,
|
|
107
|
+
types: 'index.d.ts',
|
|
108
|
+
private: true,
|
|
109
|
+
license: 'MIT',
|
|
110
|
+
dependencies: {
|
|
111
|
+
[packageName]: packageJson.dependencies[packageName]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
await outputJson(join(componentOutputDir, FileName.packageJson), packageJsonContent, { spaces: 2 })
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (language === OutputLanguage.JavaScript) {
|
|
118
|
+
await generateTypings(outputDir)(tags)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
prepareDir(filePath(FileName.tempFolder), cleanTemp, verbose)
|
|
122
|
+
|
|
123
|
+
if (verbose) {
|
|
124
|
+
console.info(`${components.length} wrappers generated at ${outputDir}`)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
module.exports = {
|
|
129
|
+
generateWrappers
|
|
130
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { posix, win32 } = require('path')
|
|
2
|
+
|
|
3
|
+
const rootImportExclusions = ['vwc-tags']
|
|
4
|
+
|
|
5
|
+
module.exports.getImportsFromTag = ({ name, path }) => {
|
|
6
|
+
const normalizedPath = path.split(win32.sep).join(posix.sep)
|
|
7
|
+
const [, rootComponent] = /@vonage\/([\w-]+)\//g.exec(normalizedPath)
|
|
8
|
+
const result = []
|
|
9
|
+
if (rootComponent === name) {
|
|
10
|
+
result.push(`@vonage/${name}`)
|
|
11
|
+
} else {
|
|
12
|
+
if (rootImportExclusions.indexOf(rootComponent) < 0) {
|
|
13
|
+
result.push(`@vonage/${rootComponent}`)
|
|
14
|
+
}
|
|
15
|
+
result.push(`@vonage/${rootComponent}/${name}`)
|
|
16
|
+
}
|
|
17
|
+
return result.map(x => `import '${x}'`)
|
|
18
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
const { posix, win32 } = require('path')
|
|
2
|
+
const { getImportsFromTag } = require('./generator')
|
|
3
|
+
|
|
4
|
+
describe('getImportsFromTag', () => {
|
|
5
|
+
const mainComponentTag = {
|
|
6
|
+
name: 'vwc-list',
|
|
7
|
+
path: './../node_modules/@vonage/vwc-list/src/vwc-list.ts'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const subComponentExclusionTag = {
|
|
11
|
+
name: 'vwc-tag',
|
|
12
|
+
path: './..\\node_modules\\@vonage\\vwc-tags\\vwc-tag.d.ts'
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const subComponentTag = {
|
|
16
|
+
name: 'vwc-radio-list-item',
|
|
17
|
+
path: './../node_modules/@vonage/vwc-list/src/vwc-radio-list-item.ts'
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
it('should create a default import for main component', () => {
|
|
21
|
+
const result = getImportsFromTag(mainComponentTag)
|
|
22
|
+
|
|
23
|
+
expect(result).toEqual([`import '@vonage/vwc-list'`])
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('should create import from specific file for subcomponent and main component import', () => {
|
|
27
|
+
const result = getImportsFromTag(subComponentTag)
|
|
28
|
+
|
|
29
|
+
expect(result).toEqual([
|
|
30
|
+
`import '@vonage/vwc-list'`,
|
|
31
|
+
`import '@vonage/vwc-list/${subComponentTag.name}'`
|
|
32
|
+
])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should create import from specific file for subcomponent with exclusion', () => {
|
|
36
|
+
const result = getImportsFromTag(subComponentExclusionTag)
|
|
37
|
+
|
|
38
|
+
expect(result).toEqual([
|
|
39
|
+
`import '@vonage/vwc-tags/${subComponentExclusionTag.name}'`
|
|
40
|
+
])
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('should work for win32 paths', () => {
|
|
44
|
+
const winTag = {
|
|
45
|
+
...subComponentTag,
|
|
46
|
+
path: subComponentTag.path.split(posix.sep).join(win32.sep)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const result = getImportsFromTag(winTag)
|
|
50
|
+
|
|
51
|
+
expect(result).toEqual([
|
|
52
|
+
`import '@vonage/vwc-list'`,
|
|
53
|
+
`import '@vonage/vwc-list/${subComponentTag.name}'`
|
|
54
|
+
])
|
|
55
|
+
})
|
|
56
|
+
})
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
const { event2PropName, getProperties } = require('./utils')
|
|
2
|
+
|
|
3
|
+
// Common
|
|
4
|
+
|
|
5
|
+
const getEvents = tag => (tag.events || []).map(x => x.propName || event2PropName(x.name || x))
|
|
6
|
+
const isTypeSet = type => /(".*?" \|)/.test(type)
|
|
7
|
+
const isStringTypeSet = type => /".*"[ |]?/.test(type) && /".*"[ |]?/.exec(type)[0] === type
|
|
8
|
+
const isBoolean = type => /^(true|false)$/.test(type) || type === 'boolean'
|
|
9
|
+
const isArray = type => /(.*)\[\]$/gm.test(type) || type === 'array'
|
|
10
|
+
const isNumber = type => /(integer)/.test(type) || type === 'number | null'
|
|
11
|
+
|
|
12
|
+
// TypeScript
|
|
13
|
+
|
|
14
|
+
const getProps = tag => {
|
|
15
|
+
const eventsProps = getEvents(tag).map(x => ` ${x}?: (event: SyntheticEvent) => void`)
|
|
16
|
+
|
|
17
|
+
const properties = getProperties(tag)
|
|
18
|
+
const mapType = type => ([
|
|
19
|
+
'boolean',
|
|
20
|
+
'Boolean',
|
|
21
|
+
'string',
|
|
22
|
+
'string | null',
|
|
23
|
+
'string | undefined',
|
|
24
|
+
'string | number',
|
|
25
|
+
'number',
|
|
26
|
+
'unknown'].indexOf(type) >= 0 ||
|
|
27
|
+
isTypeSet(type))
|
|
28
|
+
? type
|
|
29
|
+
: isBoolean(type)
|
|
30
|
+
? 'boolean'
|
|
31
|
+
: isNumber(type)
|
|
32
|
+
? 'number'
|
|
33
|
+
: isArray(type)
|
|
34
|
+
? 'any[]'
|
|
35
|
+
: `any /* ${type} */`
|
|
36
|
+
const props = properties.map(x => ` ${x.name}?: ${mapType(x.type)}`)
|
|
37
|
+
|
|
38
|
+
return [
|
|
39
|
+
...eventsProps,
|
|
40
|
+
...props
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// JavaScript
|
|
45
|
+
|
|
46
|
+
const getDefaultProps = tag => {
|
|
47
|
+
const defaultProperties = getProperties(tag).filter(x => x.default)
|
|
48
|
+
return defaultProperties.map(x => ` ${x.name}: ${x.default}`)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const getPropTypes = tag => {
|
|
52
|
+
const eventsPropTypes = getEvents(tag).map(x => ` ${x}: PropTypes.func`)
|
|
53
|
+
|
|
54
|
+
const properties = getProperties(tag)
|
|
55
|
+
const isString = type => type === 'string | undefined' || type === 'string | null'
|
|
56
|
+
const getSetTypeOptions = setType => setType.split('|').map(x => x.trim())
|
|
57
|
+
const mapTypeToPropType = type => ['boolean', 'string', 'number', 'array'].indexOf(type) >= 0
|
|
58
|
+
? (type === 'boolean' ? 'bool' : type)
|
|
59
|
+
: isStringTypeSet(type)
|
|
60
|
+
? `oneOf([${getSetTypeOptions(type).join(',')}])`
|
|
61
|
+
: isString(type)
|
|
62
|
+
? 'string'
|
|
63
|
+
: isNumber(type)
|
|
64
|
+
? 'number'
|
|
65
|
+
: isArray(type)
|
|
66
|
+
? 'array'
|
|
67
|
+
: isBoolean(type)
|
|
68
|
+
? 'bool'
|
|
69
|
+
: `any /* ${type || 'type not provided'} */`
|
|
70
|
+
const propertiesPropTypes = properties.map(x =>
|
|
71
|
+
` ${x.name}: PropTypes.${mapTypeToPropType(x.type)}${x.default ? `/* default: ${x.default} */` : ''}${x.attribute ? `/* attribute: ${x.attribute} */` : ''}`)
|
|
72
|
+
|
|
73
|
+
return [
|
|
74
|
+
...eventsPropTypes,
|
|
75
|
+
...propertiesPropTypes
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
module.exports = {
|
|
80
|
+
// JavaScript
|
|
81
|
+
getDefaultProps,
|
|
82
|
+
getPropTypes,
|
|
83
|
+
|
|
84
|
+
// TypeScript
|
|
85
|
+
getProps
|
|
86
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<% banner %>
|
|
2
|
+
/*
|
|
3
|
+
Code in this file were generated using following JSON:
|
|
4
|
+
<% descriptor-json %>
|
|
5
|
+
*/
|
|
6
|
+
import PropTypes from 'prop-types'
|
|
7
|
+
import wrapper from '@vonage/vivid-react-wrapper'
|
|
8
|
+
<% imports %>
|
|
9
|
+
<% react-import %>
|
|
10
|
+
|
|
11
|
+
<% jsdoc.class %>
|
|
12
|
+
const <% component-class-name %> = wrapper('<% tag %>', {
|
|
13
|
+
events: [<% events %>],
|
|
14
|
+
attributes: [<% attributes %>],
|
|
15
|
+
properties: [<% properties %>]
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
<% component-class-name %>.propTypes = {
|
|
19
|
+
<% prop-types %>
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
<% component-class-name %>.defaultProps = {
|
|
23
|
+
<% default-props %>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
<% component-class-name %>.displayName = '<% component-class-name %>'
|
|
27
|
+
|
|
28
|
+
<% compound-components %>
|
|
29
|
+
|
|
30
|
+
export default <% component-class-name %>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import <% component-class-name %> from '../../dist/<% component-class-name %>'
|
|
3
|
+
|
|
4
|
+
export const Default = () => <<% component-class-name %> />
|
|
5
|
+
export default {
|
|
6
|
+
title: '<% component-class-name %>',
|
|
7
|
+
argTypes: {
|
|
8
|
+
//
|
|
9
|
+
// Example values
|
|
10
|
+
// numberValue: { control: 'number', defaultValue: 123 },
|
|
11
|
+
// booleanValue: { control: 'boolean', defaultValue: true },
|
|
12
|
+
// objectValue: { control: 'object', defaultValue: {} },
|
|
13
|
+
// stringValue: { control: 'string', defaultValue: 'string' },
|
|
14
|
+
// colorValue: { control: 'color' },
|
|
15
|
+
// dateValue: { control: 'date' }
|
|
16
|
+
//
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Purpose: Support code-generation activities with templates
|
|
3
|
+
*/
|
|
4
|
+
const { readFileSync } = require('fs')
|
|
5
|
+
|
|
6
|
+
const TemplateToken = {
|
|
7
|
+
BANNER: '<% banner %>',
|
|
8
|
+
IMPORTS: '<% imports %>',
|
|
9
|
+
EXPORTS: '<% exports %>',
|
|
10
|
+
TAG: '<% tag %>',
|
|
11
|
+
COMPONENT_CLASS_NAME: '<% component-class-name %>',
|
|
12
|
+
PROP_TYPES: '<% prop-types %>',
|
|
13
|
+
PROPS: '<% props %>',
|
|
14
|
+
DEFAULT_PROPS: '<% default-props %>',
|
|
15
|
+
PROPERTIES: '<% properties %>',
|
|
16
|
+
ATTRIBUTES: '<% attributes %>',
|
|
17
|
+
EVENTS: '<% events %>',
|
|
18
|
+
CLASS_JSDOC: '<% jsdoc.class %>',
|
|
19
|
+
TAG_DESCRIPTOR_JSON: '<% descriptor-json %>',
|
|
20
|
+
COMPOUND_COMPONENTS: '<% compound-components %>',
|
|
21
|
+
REACT_IMPORT: '<% react-import %>'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const GENERATED_CODE_BANNER = `/*
|
|
25
|
+
* DO NOT EDIT THIS FILE. IT IS GENERATED
|
|
26
|
+
*/`
|
|
27
|
+
const templatesFolder = `${__dirname}`
|
|
28
|
+
const getTemplate = (template, language) =>
|
|
29
|
+
readFileSync(`${templatesFolder}/${language}/${template}.tmpl`, { encoding: 'utf8' })
|
|
30
|
+
.replace(TemplateToken.BANNER, GENERATED_CODE_BANNER)
|
|
31
|
+
|
|
32
|
+
module.exports = {
|
|
33
|
+
getTemplate,
|
|
34
|
+
TemplateToken,
|
|
35
|
+
GENERATED_CODE_BANNER
|
|
36
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
<% banner %>
|
|
2
|
+
/*
|
|
3
|
+
Code in this file were generated using following JSON:
|
|
4
|
+
<% descriptor-json %>
|
|
5
|
+
*/
|
|
6
|
+
import React, { ReactNode, SyntheticEvent, ForwardRefExoticComponent } from 'react'
|
|
7
|
+
import wrapper from '@vonage/vivid-react-wrapper'
|
|
8
|
+
<% imports %>
|
|
9
|
+
|
|
10
|
+
const WrappedComponent = wrapper('<% tag %>', {
|
|
11
|
+
events: [<% events %>],
|
|
12
|
+
attributes: [<% attributes %>],
|
|
13
|
+
properties: [<% properties %>]
|
|
14
|
+
}) as ForwardRefExoticComponent<any>;
|
|
15
|
+
|
|
16
|
+
<% jsdoc.class %>
|
|
17
|
+
const <% component-class-name %> = (props:
|
|
18
|
+
{
|
|
19
|
+
children?: ReactNode,
|
|
20
|
+
slot?: string,
|
|
21
|
+
id?: string,
|
|
22
|
+
style?: React.CSSProperties,
|
|
23
|
+
ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined,
|
|
24
|
+
<% props %>
|
|
25
|
+
}
|
|
26
|
+
): JSX.Element => <WrappedComponent {...props}>{props.children}</WrappedComponent>
|
|
27
|
+
|
|
28
|
+
export default <% component-class-name %>
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
const { join, parse } = require('path')
|
|
2
|
+
const { flowRight, map, replace, reverse, startCase, uniqBy } = require('lodash/fp')
|
|
3
|
+
const { access, F_OK, readFileSync, readdirSync, existsSync, rmSync, createWriteStream } = require('fs')
|
|
4
|
+
const mkdirp = require('mkdirp')
|
|
5
|
+
const os = require('os')
|
|
6
|
+
const { spawnSync } = require('child_process')
|
|
7
|
+
const { WCAConfig, VividRepo, FileName, OutputLanguage, ComponentsBindablePropertiesMap, ComponentsReadOnlyPropertiesMap, ComponentsExtraPropertiesMap } = require('./consts')
|
|
8
|
+
const { Octokit } = require('@octokit/core')
|
|
9
|
+
const extract = require('extract-zip')
|
|
10
|
+
const { copySync } = require('fs-extra')
|
|
11
|
+
|
|
12
|
+
const renderPropertyJsDoc = tag => property => `* @param ${property.type ? `{${property.type}}` : ''} ${property.name} ${property.description ? `- ${property.description}` : ''} ${property.attribute ? `attribute: <${getComponentName(tag)} ${property.attribute} />` : ''}`
|
|
13
|
+
const renderTagPropertiesJsDoc = tag => getProperties(tag).map(renderPropertyJsDoc(tag)).join('\n')
|
|
14
|
+
const renderJsDoc = tag => `/** ${tag.description || ''} \n${renderTagPropertiesJsDoc(tag)}\n*/`
|
|
15
|
+
const stripQuotes = input => input.replace(/'/g, '')
|
|
16
|
+
const unique = stringArray => Array.from(new Set(stringArray))
|
|
17
|
+
const getGithubToken = () => process.env.GITHUB_ACCESS_TOKEN || process.env.GITHUB_TOKEN
|
|
18
|
+
const toJsonObjectsList = collection => (collection || []).map(JSON.stringify).join(',')
|
|
19
|
+
const toCommaSeparatedList = collection => (collection || []).map(x => `'${stripQuotes(x.name)}'`).join(',')
|
|
20
|
+
const capitalize = input => input.replace(/(^|\s)[a-z]/g, s => s.toUpperCase())
|
|
21
|
+
const deCapitalize = input => input.replace(/(^|\s)[A-Z]/g, s => s.toLowerCase())
|
|
22
|
+
const getComponentName = tag => capitalize(kebab2Camel(tag.name))
|
|
23
|
+
const kebab2Camel = input => deCapitalize(input.split('-').map(x => capitalize(x)).join(''))
|
|
24
|
+
const snake2Camel = input => deCapitalize(input.split('_').map(x => capitalize(x)).join(''))
|
|
25
|
+
const event2PropName = eventName => `on${capitalize(kebab2Camel(snake2Camel(eventName)))}`
|
|
26
|
+
const event2EventDescriptor = event => typeof event === 'string'
|
|
27
|
+
? ({ name: event, propName: event2PropName(event) })
|
|
28
|
+
: event
|
|
29
|
+
const getUniqueEvents = flowRight(
|
|
30
|
+
uniqBy('name'),
|
|
31
|
+
// need to reverse to have custom events first, to allow overwriting of the ones grabbed from JSDocs
|
|
32
|
+
reverse,
|
|
33
|
+
map(event2EventDescriptor)
|
|
34
|
+
)
|
|
35
|
+
const getFileNameFromDispositionHeader = input => /filename=(.*$)/.exec(input)[1]
|
|
36
|
+
const isVividPackageName = (packageName) => /@vonage\/vwc-*/.test(packageName)
|
|
37
|
+
const getIndexFileName = language => `index.${language === OutputLanguage.TypeScript ? 'tsx' : language}`
|
|
38
|
+
const getVividPackageName = componentPath => {
|
|
39
|
+
const { dir } = parse(componentPath)
|
|
40
|
+
if (dir.indexOf('node_modules') >= 0) {
|
|
41
|
+
return /(@vonage\/vwc-.*?)\//.exec(componentPath.replace(/\\/g, '/'))[1]
|
|
42
|
+
}
|
|
43
|
+
const pathParts = dir.split('\\').join('/').split('/')
|
|
44
|
+
if (pathParts.length > 0 && pathParts[pathParts.length - 1] === 'src') {
|
|
45
|
+
pathParts.pop()
|
|
46
|
+
}
|
|
47
|
+
const packageJson = filePath(join(FileName.tempFolder, ...pathParts, FileName.packageJson))
|
|
48
|
+
const pkg = getParsedJson(packageJson)
|
|
49
|
+
return pkg.name
|
|
50
|
+
}
|
|
51
|
+
const getYarnCommand = () => `yarn${os.platform() === 'win32' ? '.cmd' : ''}`
|
|
52
|
+
const prepareDir = (p, clean = true, verbose = true) => {
|
|
53
|
+
if (clean && existsSync(p)) {
|
|
54
|
+
if (verbose) {
|
|
55
|
+
console.info(`Clearing folder: ${p}`)
|
|
56
|
+
}
|
|
57
|
+
rmSync(p, { recursive: true })
|
|
58
|
+
}
|
|
59
|
+
mkdirp.sync(p)
|
|
60
|
+
}
|
|
61
|
+
const getFirstFolderNameFromPath = path => readdirSync(path, { withFileTypes: true }).find(x => x.isDirectory()).name
|
|
62
|
+
const getProperties = tag => (tag.properties || [])
|
|
63
|
+
.filter(prop => !(ComponentsReadOnlyPropertiesMap[getComponentName(tag)] || []).includes(prop.name)) // skip readonly properties
|
|
64
|
+
.filter(prop => /'.*?'/.test(prop.name) ||
|
|
65
|
+
/^([a-zA-Z_$][a-zA-Z\\d_$]*)$/.test(prop.name)) // only props having valid names
|
|
66
|
+
.map(prop => {
|
|
67
|
+
const isBindable = (ComponentsBindablePropertiesMap[getComponentName(tag)] || []).includes(prop.name) ||
|
|
68
|
+
prop.type?.indexOf('=>') > 0 || // property type is function
|
|
69
|
+
prop.type?.indexOf('[]') > 0 // property type is array
|
|
70
|
+
prop.bindable = isBindable
|
|
71
|
+
return prop
|
|
72
|
+
}).concat(ComponentsExtraPropertiesMap[getComponentName(tag)] || [])
|
|
73
|
+
|
|
74
|
+
const isFileExists = (fileName) => new Promise(
|
|
75
|
+
(resolve, reject) => access(
|
|
76
|
+
filePath(fileName),
|
|
77
|
+
F_OK,
|
|
78
|
+
error => error
|
|
79
|
+
? reject(error)
|
|
80
|
+
: resolve(fileName)
|
|
81
|
+
))
|
|
82
|
+
const filePath = (fileName) => join(process.cwd(), fileName)
|
|
83
|
+
|
|
84
|
+
const getParsedJson = (jsonFilePath) => JSON.parse(readFileSync(jsonFilePath, { encoding: 'utf8' }))
|
|
85
|
+
|
|
86
|
+
const getVividPackageNames = ({ dependencies, devDependencies }) => {
|
|
87
|
+
const packages = [
|
|
88
|
+
...Object.keys(dependencies),
|
|
89
|
+
...Object.keys(devDependencies)
|
|
90
|
+
]
|
|
91
|
+
const result = unique(packages).filter(isVividPackageName)
|
|
92
|
+
console.log(`Vivid packages detected from ${FileName.packageJson}: \n${result.map(x => ` - ${x}`).join('\n')}`)
|
|
93
|
+
return result
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const getCustomElementTagsDefinitionsList = (config = WCAConfig) => (vividPackageNames) => new Promise((resolve) => {
|
|
97
|
+
const analyzerOutput = filePath(join(config.tempFolder, config.tempFileName))
|
|
98
|
+
const child = spawnSync(
|
|
99
|
+
'node',
|
|
100
|
+
config.nodeArgumentsFactory(vividPackageNames, analyzerOutput),
|
|
101
|
+
{ cwd: process.cwd() }
|
|
102
|
+
)
|
|
103
|
+
if (child.status === 0) {
|
|
104
|
+
const output = getParsedJson(analyzerOutput)
|
|
105
|
+
const uniqueTags = unique(output.tags.map(x => x.name)).map(x => output.tags.find(y => y.name === x))
|
|
106
|
+
return resolve(uniqueTags)
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const compileTypescript = (rootDir) => async (outDir) =>
|
|
111
|
+
spawnSync(
|
|
112
|
+
'node',
|
|
113
|
+
[
|
|
114
|
+
'./node_modules/typescript/lib/tsc.js',
|
|
115
|
+
'--project',
|
|
116
|
+
filePath('tsconfig.json'),
|
|
117
|
+
'--rootDir',
|
|
118
|
+
rootDir,
|
|
119
|
+
'--outDir',
|
|
120
|
+
outDir
|
|
121
|
+
]
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
const copyStaticAssets = (outputDir, assets) => () => {
|
|
125
|
+
const cp = file => {
|
|
126
|
+
const source = filePath(file)
|
|
127
|
+
const dest = filePath(join(outputDir, file))
|
|
128
|
+
copySync(source, dest)
|
|
129
|
+
console.info(`Copy static asset ${source} => ${dest}`)
|
|
130
|
+
}
|
|
131
|
+
assets.split(',').map(assetFileName => cp(assetFileName))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const getInputArgument = (argumentName, defaultValue = null) => {
|
|
135
|
+
const argumentObjects = process.argv
|
|
136
|
+
.filter(argument => argument.indexOf('=') >= 0)
|
|
137
|
+
.map(argument => ({
|
|
138
|
+
name: argument.split('=')[0].replace(/--/g, ''),
|
|
139
|
+
value: argument.split('=')[1]
|
|
140
|
+
}))
|
|
141
|
+
const targetArgument = argumentObjects.find(argumentObject => argumentObject.name === argumentName)
|
|
142
|
+
return targetArgument ? targetArgument.value : defaultValue
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const getVividLatestRelease = async (config = { tempFolder: FileName.tempFolder, tempFileName: FileName.tempVividZipball }) => {
|
|
146
|
+
const outFolder = filePath(config.tempFolder)
|
|
147
|
+
prepareDir(outFolder, false)
|
|
148
|
+
console.log('Fetching latest Vivid release artifact...')
|
|
149
|
+
if (!getGithubToken()) {
|
|
150
|
+
console.warn('It seems GITHUB_ACCESS_TOKEN or GITHUB_TOKEN environment variable is not defined.')
|
|
151
|
+
return
|
|
152
|
+
}
|
|
153
|
+
const octokit = new Octokit({ auth: getGithubToken() })
|
|
154
|
+
const result = await octokit.request(`GET /repos/${VividRepo}/zipball`)
|
|
155
|
+
if (result.status === 200) {
|
|
156
|
+
const filename = getFileNameFromDispositionHeader(result.headers['content-disposition'])
|
|
157
|
+
console.info(`Got zipball ${filename}`)
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const vividZipFileName = join(outFolder, config.tempFileName)
|
|
160
|
+
const vividZipStream = createWriteStream(vividZipFileName)
|
|
161
|
+
vividZipStream.write(Buffer.from(result.data), async () => {
|
|
162
|
+
try {
|
|
163
|
+
await extract(vividZipFileName, { dir: outFolder })
|
|
164
|
+
} catch (err) {
|
|
165
|
+
console.error(err)
|
|
166
|
+
reject(err)
|
|
167
|
+
}
|
|
168
|
+
const vividFolder = join(outFolder, getFirstFolderNameFromPath(outFolder))
|
|
169
|
+
console.log(`Installing Vivid packages at: ${vividFolder}...`)
|
|
170
|
+
const child = spawnSync(getYarnCommand(), [], { cwd: vividFolder, stdio: 'ignore' })
|
|
171
|
+
if (child.status === 0) {
|
|
172
|
+
console.log('Analyzing Vivid elements...')
|
|
173
|
+
resolve(`${vividFolder}/**/components/**`)
|
|
174
|
+
}
|
|
175
|
+
resolve()
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const getComponentNameFromPackage = flowRight(
|
|
182
|
+
replace(/\s/g, ''),
|
|
183
|
+
startCase,
|
|
184
|
+
replace('@vonage/', '')
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
const compoundComponentTemplate = (baseName, compositeName, defaultProps) =>
|
|
188
|
+
`const ${compositeName} = (props) => createElement(${baseName}, props)
|
|
189
|
+
|
|
190
|
+
${compositeName}.defaultProps = ${JSON.stringify(defaultProps)}
|
|
191
|
+
|
|
192
|
+
${baseName}.${compositeName} = ${compositeName}`
|
|
193
|
+
|
|
194
|
+
const prepareCompoundComponents = (baseName, template, compoundComponentsConfig = {}) => () => Object
|
|
195
|
+
.entries(compoundComponentsConfig)
|
|
196
|
+
.map(([compositeName, defaultProps]) => template(baseName, compositeName, defaultProps))
|
|
197
|
+
.join('\n\n')
|
|
198
|
+
|
|
199
|
+
module.exports = {
|
|
200
|
+
toCommaSeparatedList,
|
|
201
|
+
toJsonObjectsList,
|
|
202
|
+
compileTypescript,
|
|
203
|
+
filePath,
|
|
204
|
+
prepareDir,
|
|
205
|
+
renderJsDoc,
|
|
206
|
+
capitalize,
|
|
207
|
+
kebab2Camel,
|
|
208
|
+
event2PropName,
|
|
209
|
+
event2EventDescriptor,
|
|
210
|
+
getComponentNameFromPackage,
|
|
211
|
+
getInputArgument,
|
|
212
|
+
getUniqueEvents,
|
|
213
|
+
isFileExists,
|
|
214
|
+
isVividPackageName,
|
|
215
|
+
copyStaticAssets,
|
|
216
|
+
getIndexFileName,
|
|
217
|
+
getProperties,
|
|
218
|
+
getComponentName,
|
|
219
|
+
getParsedJson,
|
|
220
|
+
getVividPackageName,
|
|
221
|
+
getVividPackageNames,
|
|
222
|
+
getVividLatestRelease,
|
|
223
|
+
getCustomElementTagsDefinitionsList,
|
|
224
|
+
prepareCompoundComponents,
|
|
225
|
+
compoundComponentTemplate
|
|
226
|
+
}
|