* refactor(codegen): share syntax-tree code generation between exporters dom-css printed Tailwind JSX with its own esrap JSX builders and kept TypeScript template helpers under its Storybook export. The OpenPencil JSX exporter needs the same JSX builders, and design-jsx and dom-css may not depend on each other. @open-pencil/codegen holds both: es for ESTree templates and modules (moved from dom-css) and jsx for JSX elements, attributes, text, and printing, including the literal rules that keep exported strings from being reinterpreted. dom-css no longer depends on acorn and esrap directly. * refactor(design-jsx): print JSX export as syntax trees sceneNodeToJSX concatenated strings with hand-written escaping and indentation. It now collects typed props (moved to export/props.ts) and prints them with @open-pencil/codegen's JSX builders. Output is unchanged except for text: special characters print as a string expression instead of entities, and multi-line text keeps its line breaks, which the old line-splitting lost on render. * docs(codegen): fix package metadata and dependency rules codegen's repository.directory still named design-jsx, the README mentioned es without showing it, and the design-jsx and dom-css guides still said they depend only on scene-graph. * test(core): move the JSX export round-trips to the design-jsx test home Covers tabs in layer names and text, which JSX keeps as written.
49 lines
1.5 KiB
TypeScript
49 lines
1.5 KiB
TypeScript
import { describe, expect, test } from 'bun:test'
|
|
|
|
import { jsx } from '#codegen/index'
|
|
|
|
const card = (attributes: ReturnType<typeof jsx.attribute>[], children = [jsx.text('Hi')]) =>
|
|
jsx.printJSX(jsx.element('Card', attributes, children, 0, true))
|
|
|
|
describe('JSX attributes', () => {
|
|
test('keep plain strings quoted', () => {
|
|
expect(card([jsx.attribute('title', jsx.stringValue('Plain name'))])).toBe(
|
|
'<Card title="Plain name">Hi</Card>'
|
|
)
|
|
})
|
|
|
|
test.each(['a" w={999} x="', 'Fish & chips', 'Back\\slash', 'Two\nlines'])(
|
|
'write %p as a string literal JSX cannot reinterpret',
|
|
(value) => {
|
|
const code = card([jsx.attribute('title', jsx.stringValue(value))])
|
|
expect(code).toBe(`<Card title={${JSON.stringify(value)}}>Hi</Card>`)
|
|
}
|
|
)
|
|
|
|
test('print a null value as the bare name', () => {
|
|
expect(card([jsx.attribute('wide', null)], [])).toBe('<Card wide />')
|
|
})
|
|
})
|
|
|
|
describe('JSX text', () => {
|
|
test('keeps plain text as text', () => {
|
|
expect(card([], [jsx.text('Hello')])).toBe('<Card>Hello</Card>')
|
|
})
|
|
|
|
test.each(['{braces}', '<tag>', 'a & b', 'one\ntwo', ' padded '])(
|
|
'writes %p as a string expression',
|
|
(value) => {
|
|
expect(card([], [jsx.text(value)])).toBe(`<Card>{${JSON.stringify(value)}}</Card>`)
|
|
}
|
|
)
|
|
})
|
|
|
|
describe('JSX elements', () => {
|
|
test('put children on indented lines', () => {
|
|
const child = jsx.element('Item', [], [], 1)
|
|
expect(jsx.printJSX(jsx.element('List', [], [child, child], 0))).toBe(
|
|
'<List>\n <Item />\n <Item />\n</List>'
|
|
)
|
|
})
|
|
})
|