@teleporthq/teleport-plugin-css 0.44.0-alpha.0 → 0.44.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.
- package/__tests__/component-scoped.ts +4 -2
- package/__tests__/referenced-styles.ts +61 -0
- package/__tests__/selector-paren-sanitize.ts +69 -0
- package/dist/cjs/index.d.ts +9 -1
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +510 -48
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/style-sheet.d.ts.map +1 -1
- package/dist/cjs/style-sheet.js +8 -2
- package/dist/cjs/style-sheet.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/cjs/utils.d.ts +11 -0
- package/dist/cjs/utils.d.ts.map +1 -0
- package/dist/cjs/utils.js +125 -0
- package/dist/cjs/utils.js.map +1 -0
- package/dist/esm/index.d.ts +9 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +512 -50
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/style-sheet.d.ts.map +1 -1
- package/dist/esm/style-sheet.js +9 -3
- package/dist/esm/style-sheet.js.map +1 -1
- package/dist/esm/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/utils.d.ts +11 -0
- package/dist/esm/utils.d.ts.map +1 -0
- package/dist/esm/utils.js +120 -0
- package/dist/esm/utils.js.map +1 -0
- package/package.json +5 -5
- package/src/index.ts +663 -64
- package/src/style-sheet.ts +10 -4
- package/src/utils.ts +132 -0
|
@@ -90,7 +90,7 @@ describe('Component Scoped Styles', () => {
|
|
|
90
90
|
expect(
|
|
91
91
|
jsxComponent.meta.nodesLookup.container.openingElement.attributes[0].value.expression
|
|
92
92
|
.quasis[0].value.raw
|
|
93
|
-
).toBe('md-8
|
|
93
|
+
).toBe('md-8 my-componentprimary-navbar ')
|
|
94
94
|
expect(
|
|
95
95
|
jsxComponent.meta.nodesLookup.container.openingElement.attributes[0].value.expression
|
|
96
96
|
.expressions[0].property.name
|
|
@@ -148,7 +148,9 @@ describe('Component Scoped Styles', () => {
|
|
|
148
148
|
const { chunks } = await plugin(structure)
|
|
149
149
|
const hastComponent = chunks.find((chunk) => chunk.name === 'template')
|
|
150
150
|
|
|
151
|
-
expect(hastComponent.meta.nodesLookup.container.properties['class']).toBe(
|
|
151
|
+
expect(hastComponent.meta.nodesLookup.container.properties['class']).toBe(
|
|
152
|
+
'md-8 my-componentprimary-navbar'
|
|
153
|
+
)
|
|
152
154
|
expect(hastComponent.meta.nodesLookup.container.properties['v-bind:class']).toBe('variant')
|
|
153
155
|
})
|
|
154
156
|
})
|
|
@@ -199,3 +199,64 @@ describe('Referenced Styles for inlined and project-referenced with JSX bases No
|
|
|
199
199
|
expect(cssFile.content).toContain('@media(max-width: 991px)')
|
|
200
200
|
})
|
|
201
201
|
})
|
|
202
|
+
|
|
203
|
+
describe('An element’s own styles, weighed like the Next export’s (doubleElementClass)', () => {
|
|
204
|
+
const run = async (doubleElementClass: boolean) => {
|
|
205
|
+
const plugin = createCSSPlugin({ templateChunkName: 'template', doubleElementClass })
|
|
206
|
+
const componentChunk: ChunkDefinition = setUpHASTChunk()
|
|
207
|
+
const referencedStyles: UIDLReferencedStyles = {
|
|
208
|
+
narrow: {
|
|
209
|
+
type: 'style-map',
|
|
210
|
+
content: {
|
|
211
|
+
mapType: 'inlined',
|
|
212
|
+
conditions: [{ conditionType: 'screen-size', maxWidth: 991 }],
|
|
213
|
+
styles: { color: staticNode('green') },
|
|
214
|
+
},
|
|
215
|
+
},
|
|
216
|
+
hovered: {
|
|
217
|
+
type: 'style-map',
|
|
218
|
+
content: {
|
|
219
|
+
mapType: 'inlined',
|
|
220
|
+
conditions: [{ conditionType: 'element-state', content: 'hover' }],
|
|
221
|
+
styles: { color: staticNode('orange') },
|
|
222
|
+
},
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
const element = elementNode(
|
|
226
|
+
'container',
|
|
227
|
+
{},
|
|
228
|
+
[],
|
|
229
|
+
null,
|
|
230
|
+
{ color: staticNode('white') },
|
|
231
|
+
null,
|
|
232
|
+
referencedStyles
|
|
233
|
+
)
|
|
234
|
+
element.content.key = 'container'
|
|
235
|
+
const result = await plugin({
|
|
236
|
+
uidl: component('test', element),
|
|
237
|
+
options: {},
|
|
238
|
+
chunks: [componentChunk],
|
|
239
|
+
dependencies: {},
|
|
240
|
+
})
|
|
241
|
+
const css = result.chunks.find((chunk) => chunk.fileType === FileType.CSS)?.content ?? ''
|
|
242
|
+
return { css, classAttr: componentChunk.meta.nodesLookup.container.properties.class }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
it('writes the class twice in every rule, so it beats a two-class combinator as it does there', async () => {
|
|
246
|
+
const { css, classAttr } = await run(true)
|
|
247
|
+
expect(css).toMatch(/\.container\.container\s*\{\s*color: white;/)
|
|
248
|
+
expect(css).toMatch(/\.container\.container:hover\s*\{\s*color: orange;/)
|
|
249
|
+
expect(css).toMatch(
|
|
250
|
+
/@media\(max-width: 991px\)\s*\{\s*\.container\.container\s*\{\s*color: green;/
|
|
251
|
+
)
|
|
252
|
+
// the element wears it once, and no escaped dot ever reaches the stylesheet
|
|
253
|
+
expect(classAttr).toBe('container')
|
|
254
|
+
expect(css).not.toContain('\\.')
|
|
255
|
+
})
|
|
256
|
+
|
|
257
|
+
it('leaves every other export exactly as it was', async () => {
|
|
258
|
+
const { css } = await run(false)
|
|
259
|
+
expect(css).toMatch(/\.container\s*\{\s*color: white;/)
|
|
260
|
+
expect(css).not.toContain('.container.container')
|
|
261
|
+
})
|
|
262
|
+
})
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { UIDLStyleSetDefinition } from '@teleporthq/teleport-types'
|
|
2
|
+
import { staticNode } from '@teleporthq/teleport-uidl-builders'
|
|
3
|
+
import { sanitizeStylesheetSelectors } from '../src/utils'
|
|
4
|
+
import { createStyleSheetPlugin } from '../src/style-sheet'
|
|
5
|
+
import { setUpStructureWithHASTChunk } from './mocks'
|
|
6
|
+
|
|
7
|
+
// A stray `)` in a CSS selector makes the production cssnano selector parser throw
|
|
8
|
+
// "Expected an opening parenthesis", failing the consumer's whole `next build`,
|
|
9
|
+
// even though `next dev` silently tolerates it. The generated stylesheet must
|
|
10
|
+
// therefore never ship a selector with unbalanced parentheses.
|
|
11
|
+
describe('sanitizeStylesheetSelectors', () => {
|
|
12
|
+
it('drops an unmatched closing paren in a selector', () => {
|
|
13
|
+
expect(sanitizeStylesheetSelectors('.foo) { color: red }')).toBe('.foo { color: red }')
|
|
14
|
+
expect(sanitizeStylesheetSelectors('.a:hover) { color: red }')).toBe('.a:hover { color: red }')
|
|
15
|
+
expect(sanitizeStylesheetSelectors('.a:not(.b)) { color: red }')).toBe(
|
|
16
|
+
'.a:not(.b) { color: red }'
|
|
17
|
+
)
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
it('closes an unmatched opening paren in a selector', () => {
|
|
21
|
+
expect(sanitizeStylesheetSelectors('.a:not(.b { color: red }')).toBe(
|
|
22
|
+
'.a:not(.b ){ color: red }'
|
|
23
|
+
)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('repairs selectors nested inside @media blocks', () => {
|
|
27
|
+
expect(sanitizeStylesheetSelectors('@media (max-width: 768px) { .x) { color: red } }')).toBe(
|
|
28
|
+
'@media (max-width: 768px) { .x { color: red } }'
|
|
29
|
+
)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('leaves well-formed selectors untouched (idempotent)', () => {
|
|
33
|
+
const good = [
|
|
34
|
+
'.valid:nth-child(2n+1) { color: red }',
|
|
35
|
+
'.ok:not(.x) .y { color: green }',
|
|
36
|
+
'.btn:hover:not(:disabled) { color: blue }',
|
|
37
|
+
'[data-x=")"] { color: red }',
|
|
38
|
+
'@media (max-width: 991px) { .a { color: red } }',
|
|
39
|
+
]
|
|
40
|
+
good.forEach((css) => {
|
|
41
|
+
expect(sanitizeStylesheetSelectors(css)).toBe(css)
|
|
42
|
+
expect(sanitizeStylesheetSelectors(sanitizeStylesheetSelectors(css))).toBe(css)
|
|
43
|
+
})
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('plugin-css emits balanced selectors', () => {
|
|
48
|
+
it('repairs a malformed subselector so the stylesheet has no stray paren', async () => {
|
|
49
|
+
const plugin = createStyleSheetPlugin()
|
|
50
|
+
const structure = setUpStructureWithHASTChunk()
|
|
51
|
+
const styleSetDefinitions: Record<string, UIDLStyleSetDefinition> = {
|
|
52
|
+
cardId: {
|
|
53
|
+
type: 'reusable-project-style-map',
|
|
54
|
+
content: { color: staticNode('red') },
|
|
55
|
+
className: 'card',
|
|
56
|
+
// Stray `)` in the (unescaped) subselector — an AI-authored typo that
|
|
57
|
+
// would otherwise ship `.card:hover:not(.x)) { … }` and break cssnano.
|
|
58
|
+
subselectors: ':hover:not(.x))',
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
structure.uidl = { ...structure.uidl, styleSetDefinitions }
|
|
62
|
+
|
|
63
|
+
const { chunks } = await plugin(structure)
|
|
64
|
+
const { content } = chunks.find((chunk) => chunk.fileType === 'css')
|
|
65
|
+
|
|
66
|
+
expect(content).toContain('.card:hover:not(.x)')
|
|
67
|
+
expect(content).not.toMatch(/:not\(\.x\)\)/)
|
|
68
|
+
})
|
|
69
|
+
})
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -6,11 +6,19 @@ interface CSSPluginConfig {
|
|
|
6
6
|
componentDecoratorChunkName: string;
|
|
7
7
|
inlineStyleAttributeKey: string;
|
|
8
8
|
classAttributeName: string;
|
|
9
|
-
forceScoping: boolean;
|
|
10
9
|
templateStyle: 'html' | 'jsx';
|
|
11
10
|
declareDependency: 'import' | 'decorator' | 'none';
|
|
12
11
|
dynamicVariantPrefix?: string;
|
|
13
12
|
staticPropReferences?: boolean;
|
|
13
|
+
standaloneHtmlComponents?: boolean;
|
|
14
|
+
/**
|
|
15
|
+
* An element's own styles written under its class twice (`.title.title`),
|
|
16
|
+
* the weight of two classes. The Next export's styled-jsx scopes them with a
|
|
17
|
+
* second class and the editor weighs them the same, so an element's own
|
|
18
|
+
* colour beats a two-class combinator like `.hero .title` there; under one
|
|
19
|
+
* class it lost in the static HTML export, which showed another colour.
|
|
20
|
+
*/
|
|
21
|
+
doubleElementClass?: boolean;
|
|
14
22
|
}
|
|
15
23
|
declare const createCSSPlugin: ComponentPluginFactory<CSSPluginConfig>;
|
|
16
24
|
export { createStyleSheetPlugin, createCSSPlugin };
|
package/dist/cjs/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,sBAAsB,EACtB,eAAe,EAkBhB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAA;AAGtD,UAAU,eAAe;IACvB,SAAS,EAAE,MAAM,CAAA;IACjB,iBAAiB,EAAE,MAAM,CAAA;IACzB,2BAA2B,EAAE,MAAM,CAAA;IACnC,uBAAuB,EAAE,MAAM,CAAA;IAC/B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,EAAE,MAAM,GAAG,KAAK,CAAA;IAC7B,iBAAiB,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,CAAA;IAClD,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,wBAAwB,CAAC,EAAE,OAAO,CAAA;IAClC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAqBD,QAAA,MAAM,eAAe,EAAE,sBAAsB,CAAC,eAAe,CAs1B5D,CAAA;AAED,OAAO,EAAE,sBAAsB,EAAE,eAAe,EAAE,CAAA;;AAElD,wBAAgC"}
|