@teleporthq/teleport-plugin-css 0.40.0-alpha.0 → 0.40.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAC7D,OAAO,EAAE,SAAS,EAA2C,MAAM,4BAA4B,CAAA;AAE/F,MAAM,CAAC,IAAM,0BAA0B,GAAG,UACxC,UAAqE,EACrE,WAA+C;IAE/C,OAAO,UAAU,CAAC,GAAG,CAAC,UAAC,SAAS;QACtB,IAAA,SAAS,GAAc,SAAS,UAAvB,EAAE,OAAO,GAAK,SAAS,QAAd,CAAc;QAExC,IAAI,OAAO,KAAK,SAAS,EAAE;YACzB,OAAO,UAAG,QAAQ,CAAC,sBAAsB,CAAC,SAAS,CAAC,SAAG,YAAY,CAAC,OAAO,CAAC,CAAE,CAAA;SAC/E;QAED,OAAO,UAAG,YAAY,CAAC,WAAW,CAAC,cAAI,QAAQ,CAAC,uBAAuB,CACrE,SAAS,CACV,cAAI,YAAY,CAAC,OAAO,CAAC,CAAE,CAAA;IAC9B,CAAC,CAAC,CAAA;AACJ,CAAC,CAAA;AAED,IAAM,YAAY,GAAG,UAAC,KAAyC;IAC7D,IAAM,SAAS,GAAG,OAAO,KAAK,CAAA;IAC9B,QAAQ,SAAS,EAAE;QACjB,KAAK,QAAQ;YACX,OAAO,YAAI,KAAK,OAAG,CAAA;QACrB,KAAK,QAAQ;YACX,OAAO,KAAK,CAAA;QACd,KAAK,SAAS;YACZ,OAAO,KAAK,CAAA;QACd;YACE,MAAM,IAAI,SAAS,CACjB,uDAAgD,SAAS,uCAC1C,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAE,CACvC,CAAA;KACJ;AACH,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@teleporthq/teleport-plugin-css",
3
- "version": "0.40.0-alpha.0",
3
+ "version": "0.40.0",
4
4
  "description": "A plugin for creating the styles into external css files",
5
5
  "author": "teleportHQ",
6
6
  "license": "MIT",
@@ -25,9 +25,9 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@babel/types": "^7.5.5",
28
- "@teleporthq/teleport-plugin-common": "^0.40.0-alpha.0",
29
- "@teleporthq/teleport-shared": "^0.40.0-alpha.0",
30
- "@teleporthq/teleport-types": "^0.40.0-alpha.0"
28
+ "@teleporthq/teleport-plugin-common": "^0.40.0",
29
+ "@teleporthq/teleport-shared": "^0.40.0",
30
+ "@teleporthq/teleport-types": "^0.40.0"
31
31
  },
32
- "gitHead": "489527c223db2be2981b4291282e5c47d1e2f1db"
32
+ "gitHead": "2d167f1ad391adcc00943c69f08aef9606d82c9a"
33
33
  }
package/src/index.ts CHANGED
@@ -12,8 +12,11 @@ import {
12
12
  UIDLElementNodeReferenceStyles,
13
13
  UIDLStyleMediaQueryScreenSizeCondition,
14
14
  PluginCSS,
15
+ UIDLElement,
16
+ UIDLElementNode,
15
17
  } from '@teleporthq/teleport-types'
16
18
  import { createStyleSheetPlugin } from './style-sheet'
19
+ import { createConditionalStatement } from './utils'
17
20
 
18
21
  interface CSSPluginConfig {
19
22
  chunkName: string
@@ -21,7 +24,6 @@ interface CSSPluginConfig {
21
24
  componentDecoratorChunkName: string
22
25
  inlineStyleAttributeKey: string // style vs :style vs ...
23
26
  classAttributeName: string // class vs className
24
- forceScoping: boolean // class names get the component name prefix
25
27
  templateStyle: 'html' | 'jsx'
26
28
  declareDependency: 'import' | 'decorator' | 'none'
27
29
  dynamicVariantPrefix?: string
@@ -37,7 +39,6 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
37
39
  classAttributeName = 'class',
38
40
  templateStyle = 'html',
39
41
  declareDependency = 'none',
40
- forceScoping = false,
41
42
  dynamicVariantPrefix,
42
43
  staticPropReferences = false,
43
44
  } = config || {}
@@ -71,7 +72,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
71
72
  (chunk) => chunk.name === componentDecoratorChunkName
72
73
  )
73
74
 
74
- const templateLookup = templateChunk.meta.nodesLookup as Record<
75
+ const jsxNodesLookup = templateChunk.meta.nodesLookup as Record<
75
76
  string,
76
77
  HastNode | types.JSXElement
77
78
  >
@@ -86,27 +87,33 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
86
87
  Array<{ [x: string]: Record<string, string | number> }>
87
88
  > = {}
88
89
 
89
- UIDLUtils.traverseElements(node, (element) => {
90
+ const generateStylesForElementNode = (element: UIDLElement) => {
90
91
  const classNamesToAppend: Set<string> = new Set()
91
92
  const dynamicVariantsToAppend: Set<string> = new Set()
92
93
  const {
93
94
  style = {},
94
95
  key,
95
96
  referencedStyles = {},
96
- dependency,
97
97
  attrs = {},
98
98
  elementType,
99
+ dependency,
99
100
  } = element
100
- const elementClassName = StringUtils.camelCaseToDashCase(key)
101
101
 
102
- if (forceScoping && dependency?.type === 'local') {
102
+ const root = jsxNodesLookup[key]
103
+ if (!root) {
104
+ return
105
+ }
106
+
107
+ // Refer to line 323 all component scoped styles are appended with component name by default
108
+ if (dependency?.type === 'local') {
103
109
  StyleBuilders.setPropValueForCompStyle({
104
110
  attrs,
105
- key,
106
- jsxNodesLookup: templateLookup,
111
+ root,
107
112
  templateStyle,
113
+ // elementType is used here in-order to target the component name that the class is actually defined.
114
+ // Here we are appendigng to the node where the component is being called.
108
115
  getClassName: (styleName: string) =>
109
- getClassName(forceScoping, StringUtils.camelCaseToDashCase(elementType), styleName),
116
+ StringUtils.camelCaseToDashCase(elementType + styleName),
110
117
  })
111
118
  }
112
119
 
@@ -118,12 +125,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
118
125
  return
119
126
  }
120
127
 
121
- const root = templateLookup[key]
122
- if (!root) {
123
- throw new PluginCSS(`Node - ${key} is missing from the template chunk`)
124
- }
125
-
126
- const className = getClassName(forceScoping, uidl.name, elementClassName)
128
+ const className = StringUtils.camelCaseToDashCase(key)
127
129
 
128
130
  const { staticStyles, dynamicStyles, tokenStyles } =
129
131
  UIDLUtils.splitDynamicAndStaticStyles(style)
@@ -206,24 +208,21 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
206
208
  propDefinitions[styleRef.content.content.content.id]?.defaultValue
207
209
 
208
210
  if (defaultPropValue) {
209
- /* Changing the default value of the prop.
210
- When forceScoping is enabled the classnames change. So, we need to change the default prop too. */
211
- propDefinitions[styleRef.content.content.content.id].defaultValue = getClassName(
212
- forceScoping,
213
- uidl.name,
214
- String(defaultPropValue)
215
- )
211
+ propDefinitions[styleRef.content.content.content.id].defaultValue =
212
+ StringUtils.camelCaseToDashCase(String(defaultPropValue))
216
213
  }
217
214
 
215
+ // staticPropReferences flag is only used for just html-code generation.
216
+ // This is used to append the class name to the node where the component is being called.
217
+ // Instead of how frameworks handle them using props at runtime. This makes the props
218
+ // to behave statically during the generation time instead by appending them directly.
218
219
  if (staticPropReferences) {
219
- if (!defaultPropValue) {
220
+ if (defaultPropValue === undefined || typeof defaultPropValue !== 'string') {
220
221
  return
221
222
  }
222
- if (staticPropReferences) {
223
- classNamesToAppend.add(
224
- getClassName(forceScoping, uidl.name, String(defaultPropValue))
225
- )
226
- }
223
+ classNamesToAppend.add(
224
+ StringUtils.camelCaseToDashCase(uidl.name + defaultPropValue)
225
+ )
227
226
  } else {
228
227
  dynamicVariantsToAppend.add(styleRef.content.content.content.id)
229
228
  }
@@ -239,7 +238,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
239
238
  )
240
239
  }
241
240
  classNamesToAppend.add(
242
- getClassName(forceScoping, uidl.name, String(styleRef.content.content.content.id))
241
+ StringUtils.camelCaseToDashCase(uidl.name + styleRef.content.content.content.id)
243
242
  )
244
243
  }
245
244
 
@@ -255,7 +254,64 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
255
254
  )
256
255
  }
257
256
 
258
- classNamesToAppend.add(content.referenceId)
257
+ if (styleRef.content.condition) {
258
+ const {
259
+ value: staticValue,
260
+ reference,
261
+ expression: { conditions, matchingCriteria },
262
+ } = styleRef.content.condition
263
+
264
+ const {
265
+ content: { referenceType, id, refPath = [] },
266
+ } = reference
267
+
268
+ switch (referenceType) {
269
+ case 'prop': {
270
+ const usedProp = propDefinitions[id]
271
+ if (usedProp === undefined || usedProp.defaultValue === undefined) {
272
+ throw new PluginCSS(`Prop with ${id} is missing in the propDefinitions.`)
273
+ }
274
+
275
+ let defaultValue = usedProp.defaultValue
276
+ for (const path of refPath) {
277
+ defaultValue = (defaultValue as Record<string, unknown[]>)?.[path]
278
+ }
279
+
280
+ // If defaultValue is undefined or null after path traversal, use original default
281
+ defaultValue = defaultValue ?? usedProp.defaultValue
282
+
283
+ // Since we know the operand and the default value from the prop.
284
+ // We can try building the condition and check if the condition is true or false.
285
+ // @todo: You can only use a 'value' in UIDL or 'conditions' but not both.
286
+ // UIDL validations need to be improved on this aspect.
287
+ const dynamicConditions = createConditionalStatement(
288
+ staticValue !== undefined
289
+ ? [{ operand: staticValue, operation: '===' }]
290
+ : conditions,
291
+ defaultValue
292
+ )
293
+ const matchCondition =
294
+ matchingCriteria && matchingCriteria === 'all' ? '&&' : '||'
295
+ const conditionString = dynamicConditions.join(` ${matchCondition} `)
296
+
297
+ // tslint:disable-next-line function-constructor
298
+ const isConditionPassing = new Function(`return ${conditionString}`)()
299
+ if (isConditionPassing) {
300
+ classNamesToAppend.add(styleRef.content.referenceId)
301
+ return
302
+ }
303
+ return
304
+ }
305
+ case 'state':
306
+ throw new PluginCSS(`State reference is not supported in the project stylesheet.`)
307
+ default: {
308
+ classNamesToAppend.add(styleRef.content.referenceId)
309
+ return
310
+ }
311
+ }
312
+ } else {
313
+ classNamesToAppend.add(content.referenceId)
314
+ }
259
315
  return
260
316
  }
261
317
 
@@ -300,14 +356,26 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
300
356
  })
301
357
  )
302
358
  }
303
- })
359
+ }
360
+
361
+ UIDLUtils.traverseElements(node, generateStylesForElementNode)
362
+ for (const prop of Object.values(propDefinitions)) {
363
+ if (prop.type === 'element' && prop.defaultValue) {
364
+ UIDLUtils.traverseElements(
365
+ prop.defaultValue as UIDLElementNode,
366
+ generateStylesForElementNode
367
+ )
368
+ }
369
+ }
304
370
 
305
371
  if (Object.keys(componentStyleSet).length > 0) {
306
372
  StyleBuilders.generateStylesFromStyleSetDefinitions(
307
373
  componentStyleSet,
308
374
  cssMap,
309
375
  mediaStylesMap,
310
- (styleName: string) => getClassName(forceScoping, uidl.name, styleName)
376
+ (styleName: string) => {
377
+ return StringUtils.camelCaseToDashCase(uidl.name + styleName)
378
+ }
311
379
  )
312
380
  }
313
381
 
@@ -348,7 +416,7 @@ const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig> = (config) => {
348
416
  type: ChunkType.STRING,
349
417
  name: chunkName,
350
418
  fileType: FileType.CSS,
351
- content: cssMap.join('\n'),
419
+ content: cssMap.join('\n \n'),
352
420
  linkAfter: [],
353
421
  })
354
422
  }
@@ -370,9 +438,3 @@ const createDynamicInlineStyle = (styles: UIDLStyleDefinitions) => {
370
438
  })
371
439
  .join(', ')
372
440
  }
373
-
374
- const getClassName = (scoping: boolean, uidlName: string, nodeStyleName: string) => {
375
- return scoping
376
- ? StringUtils.camelCaseToDashCase(`${uidlName}-${nodeStyleName}`)
377
- : StringUtils.camelCaseToDashCase(nodeStyleName)
378
- }
@@ -5,22 +5,42 @@ import {
5
5
  ChunkType,
6
6
  FileType,
7
7
  } from '@teleporthq/teleport-types'
8
+ import { join, relative } from 'path'
8
9
 
9
10
  interface StyleSheetPlugin {
10
11
  fileName?: string
12
+ /*
13
+ Frameworks with dev servers don't need relative path mapping.
14
+ */
15
+ relativeFontPath?: boolean
11
16
  }
12
17
 
13
18
  export const createStyleSheetPlugin: ComponentPluginFactory<StyleSheetPlugin> = (config) => {
14
- const { fileName } = config || { fileName: 'style' }
19
+ const { fileName = 'style', relativeFontPath = false } = config || {
20
+ fileName: 'style',
21
+ relativeFontPath: false,
22
+ }
23
+
15
24
  const styleSheetPlugin: ComponentPlugin = async (structure) => {
16
- const { uidl, chunks } = structure
25
+ const {
26
+ uidl,
27
+ chunks,
28
+ options: { assets },
29
+ } = structure
17
30
  const { styleSetDefinitions = {}, designLanguage: { tokens = {} } = {} } = uidl
31
+ const {
32
+ localFonts = [],
33
+ fontsFolder,
34
+ identifier: assetIdentifier,
35
+ } = assets || { localFonts: [] }
18
36
 
19
- if (
20
- (!styleSetDefinitions && !tokens) ||
21
- (Object.keys(styleSetDefinitions).length === 0 && Object.keys(tokens).length === 0)
22
- ) {
23
- return
37
+ const canSkipThePlugin =
38
+ localFonts?.length === 0 &&
39
+ Object.keys(styleSetDefinitions).length === 0 &&
40
+ Object.keys(tokens).length === 0
41
+
42
+ if (canSkipThePlugin) {
43
+ return structure
24
44
  }
25
45
 
26
46
  const cssMap: string[] = []
@@ -29,6 +49,35 @@ export const createStyleSheetPlugin: ComponentPluginFactory<StyleSheetPlugin> =
29
49
  Array<{ [x: string]: Record<string, string | number> }>
30
50
  > = {}
31
51
 
52
+ uidl.outputOptions = uidl.outputOptions || {}
53
+ uidl.outputOptions.styleFileName = fileName
54
+
55
+ if (localFonts.length > 0) {
56
+ localFonts.forEach((font) => {
57
+ const properties = StyleUtils.getContentOfStyleObject(font.properties) as Record<
58
+ string,
59
+ string | number
60
+ >
61
+ const format = properties?.format ? `format('${properties.format}')` : ''
62
+ /* tslint:disable:no-string-literal */
63
+ delete properties['format']
64
+
65
+ const fontPath = relativeFontPath
66
+ ? join(
67
+ relative(join(...(uidl.outputOptions?.folderPath || [])), './'),
68
+ join(fontsFolder || '', font.path)
69
+ )
70
+ : join('/', assetIdentifier || '', 'fonts', font.path)
71
+
72
+ cssMap.push(
73
+ StyleBuilders.createFontDecleration({
74
+ ...properties,
75
+ src: `url("${fontPath}") ${format}`,
76
+ })
77
+ )
78
+ })
79
+ }
80
+
32
81
  if (Object.keys(tokens).length > 0) {
33
82
  cssMap.push(
34
83
  StyleBuilders.createCSSClassWithSelector(
@@ -44,7 +93,7 @@ export const createStyleSheetPlugin: ComponentPluginFactory<StyleSheetPlugin> =
44
93
  styleSetDefinitions,
45
94
  cssMap,
46
95
  mediaStylesMap,
47
- (styleName: string) => styleName
96
+ (styleName) => styleName
48
97
  )
49
98
  }
50
99
 
@@ -54,14 +103,11 @@ export const createStyleSheetPlugin: ComponentPluginFactory<StyleSheetPlugin> =
54
103
  return structure
55
104
  }
56
105
 
57
- uidl.outputOptions = uidl.outputOptions || {}
58
- uidl.outputOptions.styleFileName = fileName
59
-
60
106
  chunks.push({
61
107
  name: fileName,
62
108
  type: ChunkType.STRING,
63
109
  fileType: FileType.CSS,
64
- content: cssMap.join('\n'),
110
+ content: cssMap.join('\n \n'),
65
111
  linkAfter: [],
66
112
  })
67
113
 
package/src/utils.ts ADDED
@@ -0,0 +1,36 @@
1
+ import { ASTUtils } from '@teleporthq/teleport-plugin-common'
2
+ import { PluginCSS, UIDLConditionalNode, UIDLPropDefinition } from '@teleporthq/teleport-types'
3
+
4
+ export const createConditionalStatement = (
5
+ conditions: UIDLConditionalNode['content']['condition']['conditions'],
6
+ leftOperand: UIDLPropDefinition['defaultValue']
7
+ ) => {
8
+ return conditions.map((condition) => {
9
+ const { operation, operand } = condition
10
+
11
+ if (operand === undefined) {
12
+ return `${ASTUtils.convertToUnaryOperator(operation)}${getValueType(operand)}`
13
+ }
14
+
15
+ return `${getValueType(leftOperand)} ${ASTUtils.convertToBinaryOperator(
16
+ operation
17
+ )} ${getValueType(operand)}`
18
+ })
19
+ }
20
+
21
+ const getValueType = (value: UIDLPropDefinition['defaultValue']) => {
22
+ const valueType = typeof value
23
+ switch (valueType) {
24
+ case 'string':
25
+ return `"${value}"`
26
+ case 'number':
27
+ return value
28
+ case 'boolean':
29
+ return value
30
+ default:
31
+ throw new PluginCSS(
32
+ `Conditional node received an operand of type ${valueType} \n
33
+ Received ${JSON.stringify(value)}`
34
+ )
35
+ }
36
+ }