@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.
Files changed (101) hide show
  1. package/.storybook/main.js +11 -0
  2. package/.storybook/preview.js +3 -0
  3. package/README.md +183 -0
  4. package/babel.config.js +38 -0
  5. package/package.json +135 -7
  6. package/rollup.config.js +30 -0
  7. package/scripts/cli.js +28 -0
  8. package/scripts/generateWrappers/consts.js +226 -0
  9. package/scripts/generateWrappers/generator.js +130 -0
  10. package/scripts/generateWrappers/helpers/generator.js +18 -0
  11. package/scripts/generateWrappers/helpers/generator.test.js +56 -0
  12. package/scripts/generateWrappers/index.js +0 -0
  13. package/scripts/generateWrappers/prop.types.js +86 -0
  14. package/scripts/generateWrappers/templates/js/index.tmpl +2 -0
  15. package/scripts/generateWrappers/templates/js/react-component.tmpl +30 -0
  16. package/scripts/generateWrappers/templates/js/stories.tmpl +18 -0
  17. package/scripts/generateWrappers/templates/templates.js +36 -0
  18. package/scripts/generateWrappers/templates/ts/index.tmpl +2 -0
  19. package/scripts/generateWrappers/templates/ts/react-component.tmpl +28 -0
  20. package/scripts/generateWrappers/utils.js +226 -0
  21. package/scripts/generateWrappers/utils.test.js +71 -0
  22. package/scripts/updateDocs.js +42 -0
  23. package/scripts/updateVividPackages.js +81 -0
  24. package/src/dataGrid/index.js +15 -0
  25. package/src/index.js +3 -0
  26. package/src/initialization/initVivid.js +24 -0
  27. package/src/patches/VwcCard/index.js +2 -0
  28. package/src/patches/VwcCard/vwcCardHeadingCrop.js +28 -0
  29. package/src/patches/VwcCard/vwcCardWithMenu.js +14 -0
  30. package/src/patches/index.js +6 -0
  31. package/src/patches/vwcDataGrid.js +26 -0
  32. package/src/patches/vwcNoteContentOverflow.js +20 -0
  33. package/src/patches/vwcSetAttributeValue.js +12 -0
  34. package/src/patches/vwcSnackBarHtmlMessage.js +21 -0
  35. package/src/patches/vwcTooltip.js +46 -0
  36. package/stories/VwcAccordion/VwcAccordion.stories.jsx +18 -0
  37. package/stories/VwcActionGroup/VwcActionGroup.stories.jsx +18 -0
  38. package/stories/VwcBadge/VwcBadge.stories.jsx +18 -0
  39. package/stories/VwcBanner/VwcBanner.stories.jsx +18 -0
  40. package/stories/VwcButton/VwcButton.stories.jsx +18 -0
  41. package/stories/VwcButtonToggleGroup/VwcButtonToggleGroup.stories.jsx +47 -0
  42. package/stories/VwcCalendar/VwcCalendar.stories.jsx +18 -0
  43. package/stories/VwcCalendarEvent/VwcCalendarEvent.stories.jsx +18 -0
  44. package/stories/VwcCard/VwcCard.stories.jsx +90 -0
  45. package/stories/VwcCarousel/VwcCarousel.stories.jsx +18 -0
  46. package/stories/VwcCarouselItem/VwcCarouselItem.stories.jsx +18 -0
  47. package/stories/VwcCheckListItem/VwcCheckListItem.stories.jsx +18 -0
  48. package/stories/VwcCheckbox/VwcCheckbox.stories.jsx +18 -0
  49. package/stories/VwcChip/VwcChip.stories.jsx +18 -0
  50. package/stories/VwcChipSet/VwcChipSet.stories.jsx +38 -0
  51. package/stories/VwcCircularProgress/VwcCircularProgress.stories.jsx +18 -0
  52. package/stories/VwcDataGrid/VwcDataGrid.stories.jsx +109 -0
  53. package/stories/VwcDataGridColumn/VwcDataGridColumn.stories.jsx +18 -0
  54. package/stories/VwcDatepicker/VwcDatepicker.stories.jsx +18 -0
  55. package/stories/VwcDialog/VwcDialog.stories.jsx +18 -0
  56. package/stories/VwcDrawer/VwcDrawer.stories.jsx +18 -0
  57. package/stories/VwcDropdown/VwcDropdown.stories.jsx +18 -0
  58. package/stories/VwcElevation/VwcElevation.stories.jsx +18 -0
  59. package/stories/VwcEmptyState/VwcEmptyState.stories.jsx +18 -0
  60. package/stories/VwcExpansionPanel/VwcExpansionPanel.stories.jsx +18 -0
  61. package/stories/VwcFab/VwcFab.stories.jsx +18 -0
  62. package/stories/VwcFilePicker/VwcFilePicker.stories.jsx +18 -0
  63. package/stories/VwcFormfield/VwcFormfield.stories.jsx +18 -0
  64. package/stories/VwcHelperMessage/VwcHelperMessage.stories.jsx +18 -0
  65. package/stories/VwcIcon/VwcIcon.stories.jsx +18 -0
  66. package/stories/VwcIconButton/VwcIconButton.stories.jsx +18 -0
  67. package/stories/VwcIconButtonToggle/VwcIconButtonToggle.stories.jsx +18 -0
  68. package/stories/VwcKeypad/VwcKeypad.stories.jsx +18 -0
  69. package/stories/VwcLayout/VwcLayout.stories.jsx +63 -0
  70. package/stories/VwcLinearProgress/VwcLinearProgress.stories.jsx +18 -0
  71. package/stories/VwcList/VwcList.stories.jsx +25 -0
  72. package/stories/VwcListExpansionPanel/VwcListExpansionPanel.stories.jsx +18 -0
  73. package/stories/VwcListItem/VwcListItem.stories.jsx +18 -0
  74. package/stories/VwcMenu/VwcMenu.stories.jsx +41 -0
  75. package/stories/VwcNotchedOutline/VwcNotchedOutline.stories.jsx +18 -0
  76. package/stories/VwcNote/VwcNote.stories.jsx +18 -0
  77. package/stories/VwcPagination/VwcPagination.stories.jsx +18 -0
  78. package/stories/VwcPopup/VwcPopup.stories.jsx +18 -0
  79. package/stories/VwcRadio/VwcRadio.stories.jsx +18 -0
  80. package/stories/VwcRadioListItem/VwcRadioListItem.stories.jsx +18 -0
  81. package/stories/VwcSchemeSelect/VwcSchemeSelect.stories.jsx +18 -0
  82. package/stories/VwcSelect/VwcSelect.stories.jsx +18 -0
  83. package/stories/VwcSideDrawer/VwcSideDrawer.stories.jsx +18 -0
  84. package/stories/VwcSlider/VwcSlider.stories.jsx +18 -0
  85. package/stories/VwcSnackbar/VwcSnackbar.stories.jsx +18 -0
  86. package/stories/VwcSurface/VwcSurface.stories.jsx +18 -0
  87. package/stories/VwcSwitch/VwcSwitch.stories.jsx +18 -0
  88. package/stories/VwcTab/VwcTab.stories.jsx +18 -0
  89. package/stories/VwcTabBar/VwcTabBar.stories.jsx +63 -0
  90. package/stories/VwcTag/VwcTag.stories.jsx +18 -0
  91. package/stories/VwcText/VwcText.stories.jsx +18 -0
  92. package/stories/VwcTextarea/VwcTextarea.stories.jsx +24 -0
  93. package/stories/VwcTextfield/VwcTextfield.stories.jsx +47 -0
  94. package/stories/VwcThemeSwitch/VwcThemeSwitch.stories.jsx +18 -0
  95. package/stories/VwcTooltip/VwcTooltip.stories.jsx +41 -0
  96. package/stories/VwcTopAppBar/VwcTopAppBar.stories.jsx +18 -0
  97. package/stories/VwcTopAppBarFixed/VwcTopAppBarFixed.stories.jsx +18 -0
  98. package/stories/patches/patches.stories.jsx +94 -0
  99. package/testing/component.mock.js +3 -0
  100. package/tsconfig.json +70 -0
  101. 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,2 @@
1
+ <% banner %>
2
+ <% exports %>
@@ -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,2 @@
1
+ <% banner %>
2
+ <% exports %>
@@ -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: &lt;${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
+ }