@orgajs/orgx 1.0.7 → 2.0.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.
Files changed (113) hide show
  1. package/LICENSE.org +22 -0
  2. package/dist/index.d.ts +11 -5
  3. package/dist/lib/compile.d.ts +45 -0
  4. package/dist/lib/condition.browser.d.ts +1 -0
  5. package/dist/lib/condition.d.ts +1 -0
  6. package/dist/lib/core.d.ts +57 -0
  7. package/dist/lib/evaluate.d.ts +27 -0
  8. package/dist/lib/plugin/recma-document.d.ts +63 -0
  9. package/dist/lib/plugin/recma-jsx-build.d.ts +14 -0
  10. package/dist/lib/plugin/recma-jsx-rewrite.d.ts +50 -0
  11. package/dist/lib/plugin/recma-stringify.d.ts +13 -0
  12. package/dist/lib/plugin/rehype-recma.d.ts +49 -0
  13. package/dist/lib/run.d.ts +26 -0
  14. package/dist/lib/util/estree-util-create.d.ts +13 -0
  15. package/dist/lib/util/estree-util-declaration-to-expression.d.ts +19 -0
  16. package/dist/lib/util/estree-util-is-declaration.d.ts +15 -0
  17. package/dist/lib/util/estree-util-specifiers-to-declarations.d.ts +14 -0
  18. package/dist/lib/util/estree-util-to-binary-addition.d.ts +8 -0
  19. package/dist/lib/util/estree-util-to-id-or-member-expression.d.ts +11 -0
  20. package/dist/lib/util/render-error.d.ts +2 -0
  21. package/dist/lib/util/resolve-evaluate-options.d.ts +66 -0
  22. package/dist/lib/util/resolve-file-and-options.d.ts +16 -0
  23. package/index.js +14 -0
  24. package/lib/compile.js +54 -0
  25. package/lib/condition.browser.js +1 -0
  26. package/lib/condition.js +3 -0
  27. package/lib/core.js +100 -0
  28. package/lib/evaluate.js +41 -0
  29. package/lib/plugin/recma-document.js +607 -0
  30. package/lib/plugin/recma-jsx-build.js +53 -0
  31. package/lib/plugin/recma-jsx-rewrite.js +616 -0
  32. package/lib/plugin/recma-stringify.js +42 -0
  33. package/lib/plugin/rehype-recma.js +218 -0
  34. package/lib/run.js +31 -0
  35. package/lib/types.d.ts +46 -0
  36. package/lib/util/estree-util-create.js +27 -0
  37. package/lib/util/estree-util-declaration-to-expression.js +32 -0
  38. package/lib/util/estree-util-is-declaration.js +20 -0
  39. package/lib/util/estree-util-specifiers-to-declarations.js +90 -0
  40. package/lib/util/estree-util-to-binary-addition.js +23 -0
  41. package/lib/util/estree-util-to-id-or-member-expression.js +108 -0
  42. package/lib/util/render-error.js +91 -0
  43. package/lib/util/resolve-evaluate-options.js +64 -0
  44. package/lib/util/resolve-file-and-options.js +40 -0
  45. package/package.json +36 -29
  46. package/CHANGELOG.md +0 -71
  47. package/dist/compile.d.ts +0 -6
  48. package/dist/compile.d.ts.map +0 -1
  49. package/dist/compile.js +0 -12
  50. package/dist/compile.js.map +0 -1
  51. package/dist/estree/create.d.ts +0 -2
  52. package/dist/estree/create.d.ts.map +0 -1
  53. package/dist/estree/create.js +0 -14
  54. package/dist/estree/create.js.map +0 -1
  55. package/dist/estree/declaration-to-expression.d.ts +0 -3
  56. package/dist/estree/declaration-to-expression.d.ts.map +0 -1
  57. package/dist/estree/declaration-to-expression.js +0 -12
  58. package/dist/estree/declaration-to-expression.js.map +0 -1
  59. package/dist/estree/error.d.ts +0 -61
  60. package/dist/estree/error.d.ts.map +0 -1
  61. package/dist/estree/error.js +0 -87
  62. package/dist/estree/error.js.map +0 -1
  63. package/dist/estree/is-declaration.d.ts +0 -3
  64. package/dist/estree/is-declaration.d.ts.map +0 -1
  65. package/dist/estree/is-declaration.js +0 -9
  66. package/dist/estree/is-declaration.js.map +0 -1
  67. package/dist/estree/position-from-estree.d.ts +0 -13
  68. package/dist/estree/position-from-estree.d.ts.map +0 -1
  69. package/dist/estree/position-from-estree.js +0 -34
  70. package/dist/estree/position-from-estree.js.map +0 -1
  71. package/dist/estree/specifiers-to-object-pattern.d.ts +0 -5
  72. package/dist/estree/specifiers-to-object-pattern.d.ts.map +0 -1
  73. package/dist/estree/specifiers-to-object-pattern.js +0 -33
  74. package/dist/estree/specifiers-to-object-pattern.js.map +0 -1
  75. package/dist/evaluate.d.ts +0 -22
  76. package/dist/evaluate.d.ts.map +0 -1
  77. package/dist/evaluate.js +0 -27
  78. package/dist/evaluate.js.map +0 -1
  79. package/dist/index.d.ts.map +0 -1
  80. package/dist/index.js +0 -11
  81. package/dist/index.js.map +0 -1
  82. package/dist/plugin/estree-jsx-build.d.ts +0 -6
  83. package/dist/plugin/estree-jsx-build.d.ts.map +0 -1
  84. package/dist/plugin/estree-jsx-build.js +0 -38
  85. package/dist/plugin/estree-jsx-build.js.map +0 -1
  86. package/dist/plugin/estree-jsx-rewrite.d.ts +0 -6
  87. package/dist/plugin/estree-jsx-rewrite.d.ts.map +0 -1
  88. package/dist/plugin/estree-jsx-rewrite.js +0 -214
  89. package/dist/plugin/estree-jsx-rewrite.js.map +0 -1
  90. package/dist/plugin/estree-stringify.d.ts +0 -2
  91. package/dist/plugin/estree-stringify.d.ts.map +0 -1
  92. package/dist/plugin/estree-stringify.js +0 -127
  93. package/dist/plugin/estree-stringify.js.map +0 -1
  94. package/dist/plugin/estree-wrap-in-content.d.ts +0 -18
  95. package/dist/plugin/estree-wrap-in-content.d.ts.map +0 -1
  96. package/dist/plugin/estree-wrap-in-content.js +0 -375
  97. package/dist/plugin/estree-wrap-in-content.js.map +0 -1
  98. package/dist/plugin/rehype-estree.d.ts +0 -12
  99. package/dist/plugin/rehype-estree.d.ts.map +0 -1
  100. package/dist/plugin/rehype-estree.js +0 -122
  101. package/dist/plugin/rehype-estree.js.map +0 -1
  102. package/dist/plugin/rehype-set-layout.d.ts +0 -6
  103. package/dist/plugin/rehype-set-layout.d.ts.map +0 -1
  104. package/dist/plugin/rehype-set-layout.js +0 -30
  105. package/dist/plugin/rehype-set-layout.js.map +0 -1
  106. package/dist/processor.d.ts +0 -14
  107. package/dist/processor.d.ts.map +0 -1
  108. package/dist/processor.js +0 -52
  109. package/dist/processor.js.map +0 -1
  110. package/dist/utils/remove-quotes.d.ts +0 -3
  111. package/dist/utils/remove-quotes.d.ts.map +0 -1
  112. package/dist/utils/remove-quotes.js +0 -6
  113. package/dist/utils/remove-quotes.js.map +0 -1
@@ -0,0 +1,218 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').Program} Program
3
+ * @typedef {import('estree-jsx').Expression} Expression
4
+ * @typedef {import('estree-jsx').ModuleDeclaration} ModuleDeclaration
5
+ * @typedef {import('hast').Root} Root
6
+ * @typedef {import('hast-util-to-estree').Handle} Handle
7
+ * @typedef {import('hast-util-to-estree').Options} HastToEstreeOptions
8
+ */
9
+
10
+ /**
11
+ * @typedef {'html' | 'react'} ElementAttributeNameCase
12
+ * Specify casing to use for attribute names.
13
+ *
14
+ * HTML casing is for example `class`, `stroke-linecap`, `xml:lang`.
15
+ * React casing is for example `className`, `strokeLinecap`, `xmlLang`.
16
+ *
17
+ * @typedef {'css' | 'dom'} StylePropertyNameCase
18
+ * Casing to use for property names in `style` objects.
19
+ *
20
+ * CSS casing is for example `background-color` and `-webkit-line-clamp`.
21
+ * DOM casing is for example `backgroundColor` and `WebkitLineClamp`.
22
+ */
23
+
24
+ /**
25
+ * @typedef MoreOptions
26
+ * Configuration for internal plugin `rehype-recma`.
27
+ * @property {ElementAttributeNameCase | undefined | null} [elementAttributeNameCase='react']
28
+ * Specify casing to use for attribute names.
29
+ *
30
+ * This casing is used for hast elements, not for embedded Org JSX nodes
31
+ * (components that someone authored manually).
32
+ * @property {StylePropertyNameCase | undefined | null} [stylePropertyNameCase='dom']
33
+ * Specify casing to use for property names in `style` objects.
34
+ *
35
+ * This casing is used for hast elements, not for embedded Org JSX nodes
36
+ * (components that someone authored manually).
37
+ * @property {boolean} [skipImport=false] - Whether to skip imports
38
+ * @property {string[]} [parseJSX=['jsx.value']] - Specify which hast properties to parse as JSX.
39
+ *
40
+ * @typedef {HastToEstreeOptions & MoreOptions} Options
41
+ */
42
+
43
+ import { toEstree } from 'hast-util-to-estree'
44
+ import { Parser } from 'acorn'
45
+ import jsx from 'acorn-jsx'
46
+ import renderError from '../util/render-error.js'
47
+
48
+ /**
49
+ * A plugin to transform an HTML (hast) tree to a JS (estree).
50
+ * `hast-util-to-estree` does all the work for us!
51
+ *
52
+ * @type {import('unified').Plugin<[Options | undefined] | [], Root, Program>}
53
+ */
54
+ export function rehypeRecma(options = {}) {
55
+ const {
56
+ skipImport = false,
57
+ parseJSX = ['jsx.value'],
58
+ ...otherOptions
59
+ } = options
60
+ const handlers = options.handlers || {}
61
+
62
+ for (const p of parseJSX) {
63
+ const [key, ...rest] = p.split('.')
64
+ if (!key) {
65
+ throw new Error('somethings wrong')
66
+ }
67
+ const path = rest.length > 0 ? rest.join('.') : 'value'
68
+ handlers[key] = jsxHandle(path, skipImport)
69
+ }
70
+
71
+ return (tree) => {
72
+ const data = tree.data || {}
73
+ const { layout, ...otherData } = data
74
+ /** @type {ModuleDeclaration[]} */
75
+ const prepand = []
76
+ if (typeof layout === 'string') {
77
+ prepand.push({
78
+ type: 'ImportDeclaration',
79
+ specifiers: [
80
+ {
81
+ type: 'ImportDefaultSpecifier',
82
+ local: {
83
+ type: 'Identifier',
84
+ name: 'OrgaLayout',
85
+ },
86
+ },
87
+ ],
88
+ source: {
89
+ type: 'Literal',
90
+ value: `${layout}`,
91
+ raw: `'${layout}'`,
92
+ },
93
+ })
94
+ }
95
+ Object.entries(otherData).forEach(([k, v]) => {
96
+ prepand.push(createExport(k, v))
97
+ })
98
+ const estree = toEstree(tree, { ...otherOptions, handlers })
99
+ estree.body.unshift(...prepand)
100
+ return estree
101
+ }
102
+ }
103
+
104
+ /**
105
+ * @param {string} p
106
+ * @param {any} o
107
+ * @returns {any}
108
+ */
109
+ function deepGet(p, o) {
110
+ return p.split('.').reduce((a, v) => a[v], o)
111
+ }
112
+
113
+ const jsxParser = Parser.extend(jsx())
114
+
115
+ /**
116
+ * @param {string} code
117
+ */
118
+ const parse = (code) => {
119
+ try {
120
+ return jsxParser.parse(code, {
121
+ sourceType: 'module',
122
+ ecmaVersion: 2020,
123
+ })
124
+ } catch (err) {
125
+ // @ts-ignore
126
+ return renderError(err)
127
+ }
128
+ }
129
+
130
+ /**
131
+ * @param {import('acorn').Node | Program} node
132
+ * @returns {node is Program}
133
+ */
134
+ function isProgram(node) {
135
+ return node.type === 'Program'
136
+ }
137
+
138
+ /**
139
+ *
140
+ * @param {string} path
141
+ * @param {boolean} skipImport
142
+ * @returns {Handle}
143
+ */
144
+ function jsxHandle(path, skipImport) {
145
+ /** @type {Handle} */
146
+ const handle = (node, state) => {
147
+ const estree = parse(deepGet(path, node))
148
+ /** @type {Expression[]} */
149
+ const expressions = []
150
+ if (isProgram(estree)) {
151
+ estree.body.forEach((child) => {
152
+ if (child.type === 'ImportDeclaration') {
153
+ if (!skipImport) {
154
+ state.esm.push(child)
155
+ }
156
+ return false
157
+ } else if (child.type === 'ExpressionStatement') {
158
+ expressions.push(child.expression)
159
+ } else if (
160
+ child.type === 'ExportDefaultDeclaration' ||
161
+ child.type === 'ExportNamedDeclaration'
162
+ ) {
163
+ state.esm.push(child)
164
+ } else {
165
+ throw new Error(`unexpected node: ${child.type}`)
166
+ }
167
+ })
168
+ }
169
+
170
+ // @ts-ignore it works
171
+ return expressions
172
+ }
173
+ return handle
174
+ }
175
+
176
+ /**
177
+ * @param {string} text
178
+ * @returns {string}
179
+ */
180
+ function removeQuotes(text) {
181
+ return text.trim().replace(/^["'](.+(?=["']$))["']$/, '$1')
182
+ }
183
+
184
+ /**
185
+ * @param {string} k
186
+ * @param {any} v
187
+ * @returns {ModuleDeclaration}
188
+ */
189
+ function createExport(k, v) {
190
+ /** @type {(text: string) => Expression} */
191
+ const createLiteral = (text) => {
192
+ const value = removeQuotes(`${text}`)
193
+ return { type: 'Literal', value, raw: `'${value}'` }
194
+ }
195
+ /** @type {Expression} */
196
+ const init = Array.isArray(v)
197
+ ? {
198
+ type: 'ArrayExpression',
199
+ elements: v.map(createLiteral),
200
+ }
201
+ : createLiteral(v)
202
+ return {
203
+ type: 'ExportNamedDeclaration',
204
+ declaration: {
205
+ type: 'VariableDeclaration',
206
+ declarations: [
207
+ {
208
+ type: 'VariableDeclarator',
209
+ id: { type: 'Identifier', name: k },
210
+ init,
211
+ },
212
+ ],
213
+ kind: 'const',
214
+ },
215
+ specifiers: [],
216
+ source: null,
217
+ }
218
+ }
package/lib/run.js ADDED
@@ -0,0 +1,31 @@
1
+ /** @type {new (code: string, ...args: Array<unknown>) => Function} **/
2
+ const AsyncFunction = Object.getPrototypeOf(run).constructor
3
+
4
+ /**
5
+ * Asynchronously run code.
6
+ *
7
+ * @param {{toString(): string}} file
8
+ * JS document to run.
9
+ * @param {unknown} options
10
+ * Parameter.
11
+ * @return {Promise<any>}
12
+ * Anything.
13
+ */
14
+ export async function run(file, options) {
15
+ return new AsyncFunction(String(file))(options)
16
+ }
17
+
18
+ /**
19
+ * Synchronously run code.
20
+ *
21
+ * @param {{toString(): string}} file
22
+ * JS document to run.
23
+ * @param {unknown} options
24
+ * Parameter.
25
+ * @return {any}
26
+ * Anything.
27
+ */
28
+ export function runSync(file, options) {
29
+ // eslint-disable-next-line no-new-func
30
+ return new Function(String(file))(options)
31
+ }
package/lib/types.d.ts ADDED
@@ -0,0 +1,46 @@
1
+ type FunctionComponent<Props> = (props: Props) => JSX.Element | null
2
+ type ClassComponent<Props> = new (props: Props) => JSX.ElementClass
3
+ type Component<Props> =
4
+ | FunctionComponent<Props>
5
+ | ClassComponent<Props>
6
+ | keyof JSX.IntrinsicElements
7
+
8
+ interface NestedOrgComponents {
9
+ [key: string]: NestedOrgComponents | Component<any>
10
+ }
11
+
12
+ export type OrgComponents = NestedOrgComponents & {
13
+ [Key in keyof JSX.IntrinsicElements]?: Component<JSX.IntrinsicElements[Key]>
14
+ } & {
15
+ /**
16
+ * If a wrapper component is defined, the org content will be wrapped inside of it.
17
+ */
18
+ wrapper?: Component<any>
19
+ }
20
+
21
+ export interface OrgProps {
22
+ /**
23
+ * Which props exactly may be passed into the component depends on the contents of the org
24
+ * file.
25
+ */
26
+ [key: string]: unknown
27
+
28
+ /**
29
+ * This prop may be used to customize how certain components are rendered.
30
+ */
31
+ components?: OrgComponents
32
+ }
33
+
34
+ export type OrgContent = (props: OrgProps) => JSX.Element
35
+
36
+ export interface OrgModule {
37
+ /**
38
+ * This could be any value that is exported from the org file.
39
+ */
40
+ [key: string]: unknown
41
+
42
+ /**
43
+ * A functional JSX component which renders the content of the org file.
44
+ */
45
+ default: OrgContent
46
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').Node} Node
3
+ */
4
+
5
+ /**
6
+ * @param {Node} from
7
+ * Node to take from.
8
+ * @param {Node} to
9
+ * Node to add to.
10
+ * @returns {void}
11
+ * Nothing.
12
+ */
13
+ export function create(from, to) {
14
+ /** @type {Array<keyof Node>} */
15
+ // @ts-expect-error: `start`, `end`, `comments` are custom Acorn fields.
16
+ const fields = ['start', 'end', 'loc', 'range', 'comments']
17
+ let index = -1
18
+
19
+ while (++index < fields.length) {
20
+ const field = fields[index]
21
+
22
+ if (field in from) {
23
+ // @ts-expect-error: assume they’re settable.
24
+ to[field] = from[field]
25
+ }
26
+ }
27
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').Declaration} Declaration
3
+ * @typedef {import('estree-jsx').Expression} Expression
4
+ */
5
+
6
+ /**
7
+ * Turn a declaration into an expression.
8
+ *
9
+ * Doesn’t work for variable declarations, but that’s fine for our use case
10
+ * because currently we’re using this utility for export default declarations,
11
+ * which can’t contain variable declarations.
12
+ *
13
+ * @param {Declaration} declaration
14
+ * Declaration.
15
+ * @returns {Expression}
16
+ * Expression.
17
+ */
18
+ export function declarationToExpression(declaration) {
19
+ if (declaration.type === 'FunctionDeclaration') {
20
+ return {...declaration, type: 'FunctionExpression'}
21
+ }
22
+
23
+ if (declaration.type === 'ClassDeclaration') {
24
+ return {...declaration, type: 'ClassExpression'}
25
+ /* Internal utility so the next shouldn’t happen or a maintainer is making a
26
+ * mistake. */
27
+ /* c8 ignore next 4 */
28
+ }
29
+
30
+ // Probably `VariableDeclaration`.
31
+ throw new Error('Cannot turn `' + declaration.type + '` into an expression')
32
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').Node} Node
3
+ * @typedef {import('estree-jsx').Declaration} Declaration
4
+ */
5
+
6
+ /**
7
+ * Check if `node` is a declaration.
8
+ *
9
+ * @param {Node} node
10
+ * Node to check.
11
+ * @returns {node is Declaration}
12
+ * Whether `node` is a declaration.
13
+ */
14
+ export function isDeclaration(node) {
15
+ return Boolean(
16
+ node.type === 'FunctionDeclaration' ||
17
+ node.type === 'ClassDeclaration' ||
18
+ node.type === 'VariableDeclaration'
19
+ )
20
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').AssignmentProperty} AssignmentProperty
3
+ * @typedef {import('estree-jsx').ExportSpecifier} ExportSpecifier
4
+ * @typedef {import('estree-jsx').Expression} Expression
5
+ * @typedef {import('estree-jsx').Identifier} Identifier
6
+ * @typedef {import('estree-jsx').ImportDefaultSpecifier} ImportDefaultSpecifier
7
+ * @typedef {import('estree-jsx').ImportNamespaceSpecifier} ImportNamespaceSpecifier
8
+ * @typedef {import('estree-jsx').ImportSpecifier} ImportSpecifier
9
+ * @typedef {import('estree-jsx').VariableDeclarator} VariableDeclarator
10
+ */
11
+
12
+ import {create} from './estree-util-create.js'
13
+
14
+ /**
15
+ * @param {Array<ImportSpecifier | ImportDefaultSpecifier | ImportNamespaceSpecifier | ExportSpecifier>} specifiers
16
+ * @param {Expression} init
17
+ * @returns {Array<VariableDeclarator>}
18
+ */
19
+ export function specifiersToDeclarations(specifiers, init) {
20
+ let index = -1
21
+ /** @type {Array<VariableDeclarator>} */
22
+ const declarations = []
23
+ /** @type {Array<ImportSpecifier | ImportDefaultSpecifier | ExportSpecifier>} */
24
+ const otherSpecifiers = []
25
+ // Can only be one according to JS syntax.
26
+ /** @type {ImportNamespaceSpecifier | undefined} */
27
+ let importNamespaceSpecifier
28
+
29
+ while (++index < specifiers.length) {
30
+ const specifier = specifiers[index]
31
+
32
+ if (specifier.type === 'ImportNamespaceSpecifier') {
33
+ importNamespaceSpecifier = specifier
34
+ } else {
35
+ otherSpecifiers.push(specifier)
36
+ }
37
+ }
38
+
39
+ if (importNamespaceSpecifier) {
40
+ /** @type {VariableDeclarator} */
41
+ const declarator = {
42
+ type: 'VariableDeclarator',
43
+ id: importNamespaceSpecifier.local,
44
+ init
45
+ }
46
+ create(importNamespaceSpecifier, declarator)
47
+ declarations.push(declarator)
48
+ }
49
+
50
+ declarations.push({
51
+ type: 'VariableDeclarator',
52
+ id: {
53
+ type: 'ObjectPattern',
54
+ properties: otherSpecifiers.map((specifier) => {
55
+ /** @type {Identifier} */
56
+ let key =
57
+ specifier.type === 'ImportSpecifier'
58
+ ? specifier.imported
59
+ : specifier.type === 'ExportSpecifier'
60
+ ? specifier.exported
61
+ : {type: 'Identifier', name: 'default'}
62
+ let value = specifier.local
63
+
64
+ // Switch them around if we’re exporting.
65
+ if (specifier.type === 'ExportSpecifier') {
66
+ value = key
67
+ key = specifier.local
68
+ }
69
+
70
+ /** @type {AssignmentProperty} */
71
+ const property = {
72
+ type: 'Property',
73
+ kind: 'init',
74
+ shorthand: key.name === value.name,
75
+ method: false,
76
+ computed: false,
77
+ key,
78
+ value
79
+ }
80
+ create(specifier, property)
81
+ return property
82
+ })
83
+ },
84
+ init: importNamespaceSpecifier
85
+ ? {type: 'Identifier', name: importNamespaceSpecifier.local.name}
86
+ : init
87
+ })
88
+
89
+ return declarations
90
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').Expression} Expression
3
+ */
4
+
5
+ /**
6
+ * @param {Array<Expression>} expressions
7
+ */
8
+ export function toBinaryAddition(expressions) {
9
+ let index = -1
10
+ /** @type {Expression | undefined} */
11
+ let left
12
+
13
+ while (++index < expressions.length) {
14
+ const right = expressions[index]
15
+ left = left ? {type: 'BinaryExpression', left, operator: '+', right} : right
16
+ }
17
+
18
+ // Just for types.
19
+ /* c8 ignore next */
20
+ if (!left) throw new Error('Expected non-empty `expressions` to be passed')
21
+
22
+ return left
23
+ }
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @typedef {import('estree-jsx').Identifier} Identifier
3
+ * @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier
4
+ * @typedef {import('estree-jsx').JSXMemberExpression} JSXMemberExpression
5
+ * @typedef {import('estree-jsx').Literal} Literal
6
+ * @typedef {import('estree-jsx').MemberExpression} MemberExpression
7
+ */
8
+
9
+ import {
10
+ start as esStart,
11
+ cont as esCont,
12
+ name as isIdentifierName
13
+ } from 'estree-util-is-identifier-name'
14
+
15
+ export const toIdOrMemberExpression = toIdOrMemberExpressionFactory(
16
+ 'Identifier',
17
+ 'MemberExpression',
18
+ isIdentifierName
19
+ )
20
+
21
+ export const toJsxIdOrMemberExpression =
22
+ // @ts-expect-error: fine
23
+ /** @type {(ids: Array<string | number>) => JSXIdentifier | JSXMemberExpression)} */
24
+ (
25
+ toIdOrMemberExpressionFactory(
26
+ 'JSXIdentifier',
27
+ 'JSXMemberExpression',
28
+ isJsxIdentifierName
29
+ )
30
+ )
31
+
32
+ /**
33
+ * @param {string} idType
34
+ * @param {string} memberType
35
+ * @param {(value: string) => boolean} isIdentifier
36
+ */
37
+ function toIdOrMemberExpressionFactory(idType, memberType, isIdentifier) {
38
+ return toIdOrMemberExpression
39
+ /**
40
+ * @param {Array<string | number>} ids
41
+ * @returns {Identifier | MemberExpression}
42
+ */
43
+ function toIdOrMemberExpression(ids) {
44
+ let index = -1
45
+ /** @type {Identifier | Literal | MemberExpression | undefined} */
46
+ let object
47
+
48
+ while (++index < ids.length) {
49
+ const name = ids[index]
50
+ const valid = typeof name === 'string' && isIdentifier(name)
51
+
52
+ // A value of `asd.123` could be turned into `asd['123']` in the JS form,
53
+ // but JSX does not have a form for it, so throw.
54
+ /* c8 ignore next 3 */
55
+ if (idType === 'JSXIdentifier' && !valid) {
56
+ throw new Error('Cannot turn `' + name + '` into a JSX identifier')
57
+ }
58
+
59
+ /** @type {Identifier | Literal} */
60
+ // @ts-expect-error: JSX is fine.
61
+ const id = valid ? {type: idType, name} : {type: 'Literal', value: name}
62
+ // @ts-expect-error: JSX is fine.
63
+ object = object
64
+ ? {
65
+ type: memberType,
66
+ object,
67
+ property: id,
68
+ computed: id.type === 'Literal',
69
+ optional: false
70
+ }
71
+ : id
72
+ }
73
+
74
+ // Just for types.
75
+ /* c8 ignore next 3 */
76
+ if (!object) throw new Error('Expected non-empty `ids` to be passed')
77
+ if (object.type === 'Literal')
78
+ throw new Error('Expected identifier as left-most value')
79
+
80
+ return object
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Checks if the given string is a valid JSX identifier name.
86
+ * @param {string} name
87
+ */
88
+ function isJsxIdentifierName(name) {
89
+ let index = -1
90
+
91
+ while (++index < name.length) {
92
+ // We currently receive valid input, but this catches bugs and is needed
93
+ // when externalized.
94
+ /* c8 ignore next */
95
+ if (!(index ? jsxCont : esStart)(name.charCodeAt(index))) return false
96
+ }
97
+
98
+ // `false` if `name` is empty.
99
+ return index > 0
100
+ }
101
+
102
+ /**
103
+ * Checks if the given character code can continue a JSX identifier.
104
+ * @param {number} code
105
+ */
106
+ function jsxCont(code) {
107
+ return code === 45 /* `-` */ || esCont(code)
108
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * @param {SyntaxError} error
3
+ * @param {string} bg
4
+ * @param {string} color
5
+ * @returns {import('estree-jsx').Program}
6
+ */
7
+ export default (error, bg = '#F44336', color = 'white') => {
8
+ return {
9
+ type: 'Program',
10
+ body: [
11
+ {
12
+ type: 'ExpressionStatement',
13
+ expression: {
14
+ type: 'JSXElement',
15
+ openingElement: {
16
+ type: 'JSXOpeningElement',
17
+ attributes: [
18
+ {
19
+ type: 'JSXAttribute',
20
+ name: {
21
+ type: 'JSXIdentifier',
22
+ name: 'style',
23
+ },
24
+ value: {
25
+ type: 'JSXExpressionContainer',
26
+ expression: {
27
+ type: 'ObjectExpression',
28
+ properties: [
29
+ {
30
+ type: 'Property',
31
+ method: false,
32
+ shorthand: false,
33
+ computed: false,
34
+ key: {
35
+ type: 'Identifier',
36
+ name: 'backgroundColor',
37
+ },
38
+ value: {
39
+ type: 'Literal',
40
+ value: bg,
41
+ raw: `'${bg}'`,
42
+ },
43
+ kind: 'init',
44
+ },
45
+ {
46
+ type: 'Property',
47
+ method: false,
48
+ shorthand: false,
49
+ computed: false,
50
+ key: {
51
+ type: 'Identifier',
52
+ name: 'color',
53
+ },
54
+ value: {
55
+ type: 'Literal',
56
+ value: color,
57
+ raw: `'${color}'`,
58
+ },
59
+ kind: 'init',
60
+ },
61
+ ],
62
+ },
63
+ },
64
+ },
65
+ ],
66
+ name: {
67
+ type: 'JSXIdentifier',
68
+ name: 'pre',
69
+ },
70
+ selfClosing: false,
71
+ },
72
+ closingElement: {
73
+ type: 'JSXClosingElement',
74
+ name: {
75
+ type: 'JSXIdentifier',
76
+ name: 'pre',
77
+ },
78
+ },
79
+ children: [
80
+ {
81
+ type: 'JSXText',
82
+ value: error.message,
83
+ raw: error.message,
84
+ },
85
+ ],
86
+ },
87
+ },
88
+ ],
89
+ sourceType: 'module',
90
+ }
91
+ }