Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: syntax-tree/estree-util-build-jsx
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 2.2.0
Choose a base ref
...
head repository: syntax-tree/estree-util-build-jsx
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 2.2.2
Choose a head ref
  • 17 commits
  • 7 files changed
  • 2 contributors

Commits on Dec 26, 2022

  1. Fix initial spaces in text

    Closes GH-4.
    Closes GH-5.
    
    Reviewed-by: Titus Wormer <tituswormer@gmail.com>
    jeetiss authored Dec 26, 2022
    Copy the full SHA
    1a8b9d3 View commit details
  2. Update dev-dependencies

    wooorm committed Dec 26, 2022
    Copy the full SHA
    6eae5df View commit details
  3. Update npm scripts

    wooorm committed Dec 26, 2022
    Copy the full SHA
    53c027d View commit details
  4. Update tsconfig.json

    wooorm committed Dec 26, 2022
    Copy the full SHA
    3c78c80 View commit details
  5. Remove unneeded dev-dependency

    wooorm committed Dec 26, 2022
    Copy the full SHA
    3b60b20 View commit details
  6. Update Node in Actions

    wooorm committed Dec 26, 2022
    Copy the full SHA
    09af68b View commit details
  7. Use Node test runner

    wooorm committed Dec 26, 2022
    Copy the full SHA
    2d809f5 View commit details
  8. Add docs to types

    wooorm committed Dec 26, 2022
    Copy the full SHA
    167e6f7 View commit details
  9. Add improved types

    wooorm committed Dec 26, 2022
    Copy the full SHA
    4b8eebc View commit details
  10. 2.2.1

    wooorm committed Dec 26, 2022
    Copy the full SHA
    b52347e View commit details

Commits on Jan 7, 2023

  1. Copy the full SHA
    3044211 View commit details
  2. Refactor code-style

    *   Add support for `null` as input of API
    *   Add more docs to JSDoc
    wooorm committed Jan 7, 2023
    Copy the full SHA
    6c84d55 View commit details
  3. Add export of Runtime type

    wooorm committed Jan 7, 2023
    Copy the full SHA
    a5ad4d4 View commit details
  4. Copy the full SHA
    ead7356 View commit details
  5. Add improved docs

    wooorm committed Jan 7, 2023
    Copy the full SHA
    ac09c72 View commit details
  6. Refactor style

    wooorm committed Jan 7, 2023
    Copy the full SHA
    cb8010c View commit details
  7. 2.2.2

    wooorm committed Jan 7, 2023
    Copy the full SHA
    f762fdc View commit details
Showing with 342 additions and 195 deletions.
  1. +6 −6 .github/workflows/main.yml
  2. +2 −1 index.js
  3. +123 −48 lib/index.js
  4. +8 −10 package.json
  5. +77 −49 readme.md
  6. +117 −73 test.js
  7. +9 −8 tsconfig.json
12 changes: 6 additions & 6 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -4,18 +4,18 @@ on:
- push
jobs:
main:
name: '${{ matrix.node }}'
name: ${{matrix.node}}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: dcodeIO/setup-node-nvm@master
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node }}
node-version: ${{matrix.node}}
- run: npm install
- run: npm test
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v3
strategy:
matrix:
node:
- lts/fermium
- lts/hydrogen
- node
3 changes: 2 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/**
* @typedef {import('./lib/index.js').Node} Node
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').Runtime} Runtime
*
* @typedef {Options} BuildJsxOptions
* To do: remove next major (replaced by `Options` ).
*/

export {buildJsx} from './lib/index.js'
171 changes: 123 additions & 48 deletions lib/index.js
Original file line number Diff line number Diff line change
@@ -1,42 +1,93 @@
/**
* @typedef {import('estree-jsx').Node} Node
* @typedef {import('estree-jsx').Comment} Comment
* @typedef {import('estree-jsx').Expression} Expression
* @typedef {import('estree-jsx').Pattern} Pattern
* @typedef {import('estree-jsx').ObjectExpression} ObjectExpression
* @typedef {import('estree-jsx').Property} Property
* @typedef {import('estree-jsx').ImportSpecifier} ImportSpecifier
* @typedef {import('estree-jsx').SpreadElement} SpreadElement
* @typedef {import('estree-jsx').MemberExpression} MemberExpression
* @typedef {import('estree-jsx').Literal} Literal
* @typedef {import('estree-jsx').Identifier} Identifier
* @typedef {import('estree-jsx').JSXElement} JSXElement
* @typedef {import('estree-jsx').JSXFragment} JSXFragment
* @typedef {import('estree-jsx').JSXText} JSXText
* @typedef {import('estree-jsx').JSXExpressionContainer} JSXExpressionContainer
* @typedef {import('estree-jsx').JSXEmptyExpression} JSXEmptyExpression
* @typedef {import('estree-jsx').JSXSpreadChild} JSXSpreadChild
* @typedef {import('estree-jsx').JSXAttribute} JSXAttribute
* @typedef {import('estree-jsx').JSXSpreadAttribute} JSXSpreadAttribute
* @typedef {import('estree-jsx').JSXMemberExpression} JSXMemberExpression
* @typedef {import('estree-jsx').JSXNamespacedName} JSXNamespacedName
* @typedef {import('estree-jsx').JSXIdentifier} JSXIdentifier
*
* @typedef {import('estree-walker').SyncHandler} SyncHandler
* @typedef {'automatic' | 'classic'} Runtime
* How to transform JSX.
*
* @typedef Options
* @property {'automatic'|'classic'} [runtime='classic']
* @property {string} [importSource='react']
* @property {string} [pragma='React.createElement']
* @property {string} [pragmaFrag='React.Fragment']
* @property {boolean} [development=false]
* @property {string} [filePath]
* Configuration.
*
* > 👉 **Note**: you can also configure `runtime`, `importSource`, `pragma`,
* > and `pragmaFrag` from within files through comments.
* @property {Runtime | null | undefined} [runtime='classic']
* Choose the runtime.
*
* Comment form: `@jsxRuntime theRuntime`.
* @property {string | null | undefined} [importSource='react']
* Place to import `jsx`, `jsxs`, `jsxDEV`, and `Fragment` from, when the
* effective runtime is automatic.
*
* Comment form: `@jsxImportSource theSource`.
*
* > 👉 **Note**: `/jsx-runtime` or `/jsx-dev-runtime` is appended to this
* > provided source.
* > In CJS, that can resolve to a file (as in `theSource/jsx-runtime.js`),
* > but for ESM an export map needs to be set up to point to files:
* >
* > ```js
* > // …
* > "exports": {
* > // …
* > "./jsx-runtime": "./path/to/jsx-runtime.js",
* > "./jsx-dev-runtime": "./path/to/jsx-runtime.js"
* > // …
* > ```
* @property {string | null | undefined} [pragma='React.createElement']
* Identifier or member expression to call when the effective runtime is
* classic.
*
* Comment form: `@jsx identifier`.
* @property {string | null | undefined} [pragmaFrag='React.Fragment']
* Identifier or member expression to use as a symbol for fragments when the
* effective runtime is classic.
*
* Comment form: `@jsxFrag identifier`.
* @property {boolean | null | undefined} [development=false]
* When in the automatic runtime, whether to import
* `theSource/jsx-dev-runtime.js`, use `jsxDEV`, and pass location info when
* available.
*
* This helps debugging but adds a lot of code that you don’t want in
* production.
* @property {string | null | undefined} [filePath]
* File path to the original source file.
*
* Passed in location info to `jsxDEV` when using the automatic runtime with
* `development: true`.
*
* @typedef Annotations
* @property {'automatic'|'classic'} [jsxRuntime]
* @property {string} [jsx]
* @property {string} [jsxFrag]
* @property {string} [jsxImportSource]
* State where info from comments is gathered.
* @property {Runtime | undefined} [jsxRuntime]
* Runtime.
* @property {string | undefined} [jsx]
* JSX identifier (`pragma`).
* @property {string | undefined} [jsxFrag]
* JSX identifier of fragment (`pragmaFrag`).
* @property {string | undefined} [jsxImportSource]
* Where to import an automatic JSX runtime from.
*
* @typedef Imports
* State of used identifiers from the automatic runtime.
* @property {boolean | undefined} [fragment]
* Symbol of `Fragment`.
* @property {boolean | undefined} [jsx]
* Symbol of `jsx`.
* @property {boolean | undefined} [jsxs]
* Symbol of `jsxs`.
* @property {boolean | undefined} [jsxDEV]
* Symbol of `jsxDEV`.
*/

import {walk} from 'estree-walker'
@@ -45,20 +96,41 @@ import {name as isIdentifierName} from 'estree-util-is-identifier-name'
const regex = /@(jsx|jsxFrag|jsxImportSource|jsxRuntime)\s+(\S+)/g

/**
* @template {Node} T
* @param {T} tree
* @param {Options} [options={}]
* @returns {T}
* Turn JSX in `tree` into function calls: `<x />` -> `h('x')`!
*
* ###### Algorithm
*
* In almost all cases, this utility is the same as the Babel plugin, except that
* they work on slightly different syntax trees.
*
* Some differences:
*
* * no pure annotations things
* * `this` is not a component: `<this>` -> `h('this')`, not `h(this)`
* * namespaces are supported: `<a:b c:d>` -> `h('a:b', {'c:d': true})`,
* which throws by default in Babel or can be turned on with `throwIfNamespace`
* * no `useSpread`, `useBuiltIns`, or `filter` options
*
* @template {Node} Tree
* Node type.
* @param {Tree} tree
* Tree to transform (typically `Program`).
* @param {Options | null | undefined} [options={}]
* Configuration (optional).
* @returns {Tree}
* Given, modified, `tree`.
*/
export function buildJsx(tree, options = {}) {
let automatic = options.runtime === 'automatic'
// To do next major: do not return the given Node.
export function buildJsx(tree, options) {
const config = options || {}
let automatic = config.runtime === 'automatic'
/** @type {Annotations} */
const annotations = {}
/** @type {{fragment?: boolean, jsx?: boolean, jsxs?: boolean, jsxDEV?: boolean}} */
/** @type {Imports} */
const imports = {}

walk(tree, {
// @ts-expect-error: types are wrong.
// @ts-expect-error: hush, `estree-walker` is broken.
enter(/** @type {Node} */ node) {
if (node.type === 'Program') {
const comments = node.comments || []
@@ -107,7 +179,7 @@ export function buildJsx(tree, options = {}) {
}
}
},
// @ts-expect-error: types are wrong.
// @ts-expect-error: hush, `estree-walker` is broken.
// eslint-disable-next-line complexity
leave(/** @type {Node} */ node) {
if (node.type === 'Program') {
@@ -154,9 +226,9 @@ export function buildJsx(tree, options = {}) {
type: 'Literal',
value:
(annotations.jsxImportSource ||
options.importSource ||
config.importSource ||
'react') +
(options.development ? '/jsx-dev-runtime' : '/jsx-runtime')
(config.development ? '/jsx-dev-runtime' : '/jsx-runtime')
}
})
}
@@ -189,6 +261,8 @@ export function buildJsx(tree, options = {}) {
.replace(/\n+/g, '\n')
// Drop final line feeds.
.replace(/\n+$/, '')
// Drop first line feeds.
.replace(/^\n+/, '')
// Replace line feeds with spaces.
.replace(/\n/g, ' ')

@@ -204,15 +278,15 @@ export function buildJsx(tree, options = {}) {
}
}

/** @type {MemberExpression|Literal|Identifier} */
/** @type {MemberExpression | Literal | Identifier} */
let name
/** @type {Array<Property>} */
let fields = []
/** @type {Array<Expression>} */
const objects = []
/** @type {Array<Expression|SpreadElement>} */
/** @type {Array<Expression | SpreadElement>} */
let parameters = []
/** @type {Expression|undefined} */
/** @type {Expression | undefined} */
let key

// Do the stuff needed for elements.
@@ -225,7 +299,7 @@ export function buildJsx(tree, options = {}) {
name = create(name, {type: 'Literal', value: name.name})
}

/** @type {boolean|undefined} */
/** @type {boolean | undefined} */
let spread
const attributes = node.openingElement.attributes
let index = -1
@@ -272,7 +346,7 @@ export function buildJsx(tree, options = {}) {
name = {type: 'Identifier', name: '_Fragment'}
} else {
name = toMemberExpression(
annotations.jsxFrag || options.pragmaFrag || 'React.Fragment'
annotations.jsxFrag || config.pragmaFrag || 'React.Fragment'
)
}

@@ -299,9 +373,9 @@ export function buildJsx(tree, options = {}) {
objects.push({type: 'ObjectExpression', properties: fields})
}

/** @type {Expression|undefined} */
/** @type {Expression | undefined} */
let props
/** @type {MemberExpression|Literal|Identifier} */
/** @type {MemberExpression | Literal | Identifier} */
let callee

if (objects.length > 1) {
@@ -325,13 +399,13 @@ export function buildJsx(tree, options = {}) {

if (key) {
parameters.push(key)
} else if (options.development) {
} else if (config.development) {
parameters.push({type: 'Identifier', name: 'undefined'})
}

const isStaticChildren = children.length > 1

if (options.development) {
if (config.development) {
imports.jsxDEV = true
callee = {
type: 'Identifier',
@@ -352,7 +426,7 @@ export function buildJsx(tree, options = {}) {
key: {type: 'Identifier', name: 'fileName'},
value: {
type: 'Literal',
value: options.filePath || '<source.js>'
value: config.filePath || '<source.js>'
}
}
]
@@ -398,12 +472,13 @@ export function buildJsx(tree, options = {}) {
}

callee = toMemberExpression(
annotations.jsx || options.pragma || 'React.createElement'
annotations.jsx || config.pragma || 'React.createElement'
)
}

parameters.unshift(name)

// Types of `estree-walker` are wrong
this.replace(
create(node, {
type: 'CallExpression',
@@ -458,11 +533,11 @@ function toProperty(node) {
}

/**
* @param {JSXMemberExpression|JSXNamespacedName|JSXIdentifier} node
* @returns {MemberExpression|Identifier|Literal}
* @param {JSXMemberExpression | JSXNamespacedName | JSXIdentifier} node
* @returns {MemberExpression | Identifier | Literal}
*/
function toIdentifier(node) {
/** @type {MemberExpression|Identifier|Literal} */
/** @type {MemberExpression | Identifier | Literal} */
let replace

if (node.type === 'JSXMemberExpression') {
@@ -494,16 +569,16 @@ function toIdentifier(node) {

/**
* @param {string} id
* @returns {Identifier|Literal|MemberExpression}
* @returns {Identifier | Literal | MemberExpression}
*/
function toMemberExpression(id) {
const identifiers = id.split('.')
let index = -1
/** @type {Identifier|Literal|MemberExpression|undefined} */
/** @type {Identifier | Literal | MemberExpression | undefined} */
let result

while (++index < identifiers.length) {
/** @type {Identifier|Literal} */
/** @type {Identifier | Literal} */
const prop = isIdentifierName(identifiers[index])
? {type: 'Identifier', name: identifiers[index]}
: {type: 'Literal', value: identifiers[index]}
18 changes: 8 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "estree-util-build-jsx",
"version": "2.2.0",
"version": "2.2.2",
"description": "Transform JSX in estrees to function calls (for react, preact, and most hyperscript interfaces)",
"license": "MIT",
"keywords": [
@@ -46,30 +46,28 @@
"estree-walker": "^3.0.0"
},
"devDependencies": {
"@types/escodegen": "^0.0.6",
"@types/tape": "^4.0.0",
"@types/escodegen": "^0.0.7",
"@types/node": "^18.0.0",
"acorn": "^8.0.0",
"acorn-jsx": "^5.0.0",
"astring": "^1.0.0",
"c8": "^7.0.0",
"escodegen": "^2.0.0",
"nyc": "^15.0.0",
"prettier": "^2.0.0",
"recast": "^0.21.0",
"recast": "^0.22.0",
"remark-cli": "^11.0.0",
"remark-preset-wooorm": "^9.0.0",
"rimraf": "^3.0.0",
"tape": "^5.0.0",
"type-coverage": "^2.0.0",
"typescript": "^4.0.0",
"xo": "^0.51.0"
"xo": "^0.53.0"
},
"scripts": {
"prepack": "npm run build && npm run format",
"build": "rimraf \"lib/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage",
"build": "tsc --build --clean && tsc --build && type-coverage",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"test-api": "node test.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
"test-api": "node --conditions development test.js",
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"
},
"prettier": {
Loading