Socket
Socket
Sign inDemoInstall

micromark-util-events-to-acorn

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

micromark-util-events-to-acorn - npm Package Compare versions

Comparing version 1.2.1 to 1.2.2

129

dev/index.d.ts

@@ -5,38 +5,101 @@ /**

* @param {Array<Event>} events
* Events.
* @param {Options} options
* @returns {{estree: Program | undefined, error: Error | undefined, swallow: boolean}}
* Configuration.
* @returns {Result}
* Result.
*/
export function eventsToAcorn(events: Array<import("micromark-util-types").Event>, options: Options): {
estree: Program | undefined;
error: Error | undefined;
swallow: boolean;
};
export type Event = import('micromark-util-types').Event;
export type Point = import('micromark-util-types').Point;
export type AcornOptions = import('acorn').Options;
export type Comment = import('acorn').Comment;
export type Token = import('acorn').Token;
export type AcornNode = import('acorn').Node;
export type Program = import('estree').Program;
export type EstreeNode = import('estree').Node;
export function eventsToAcorn(
events: Array<import('micromark-util-types').Event>,
options: Options
): Result
export type Comment = import('acorn').Comment
export type AcornNode = import('acorn').Node
export type AcornOptions = import('acorn').Options
export type Token = import('acorn').Token
export type EstreeNode = import('estree').Node
export type Program = import('estree').Program
export type Chunk = import('micromark-util-types').Chunk
export type Event = import('micromark-util-types').Event
export type MicromarkPoint = import('micromark-util-types').Point
export type UnistPoint = import('unist').Point
/**
* Acorn-like interface.
*/
export type Acorn = {
parse: typeof import("acorn").parse;
parseExpressionAt: typeof import("acorn").parseExpressionAt;
};
export type AcornError = Error & {
raisedAt: number;
pos: number;
loc: {
line: number;
column: number;
};
};
/**
* Parse a program.
*/
parse: typeof import('acorn').parse
/**
* Parse an expression.
*/
parseExpressionAt: typeof import('acorn').parseExpressionAt
}
export type AcornLoc = {
line: number
column: number
}
export type AcornErrorFields = {
raisedAt: number
pos: number
loc: AcornLoc
}
export type AcornError = Error & AcornErrorFields
/**
* Configuration.
*/
export type Options = {
acorn: Acorn;
acornOptions?: AcornOptions | null | undefined;
start?: Point | null | undefined;
prefix?: string | null | undefined;
suffix?: string | null | undefined;
expression?: boolean | null | undefined;
allowEmpty?: boolean | null | undefined;
};
/**
* Typically `acorn`, object with `parse` and `parseExpressionAt` fields.
*/
acorn: Acorn
/**
* Configuration for `acorn`.
*/
acornOptions?: AcornOptions | null | undefined
/**
* Place where events start.
*/
start?: MicromarkPoint | null | undefined
/**
* Text to place before events.
*/
prefix?: string | null | undefined
/**
* Text to place after events.
*/
suffix?: string | null | undefined
/**
* Whether this is a program or expression.
*/
expression?: boolean | null | undefined
/**
* Whether an empty expression is allowed (programs are always allowed to
* be empty).
*/
allowEmpty?: boolean | null | undefined
}
/**
* Result.
*/
export type Result = {
/**
* Program.
*/
estree: Program | undefined
/**
* Error if unparseable
*/
error: AcornError | undefined
/**
* Whether the error, if there is one, can be swallowed and more JavaScript
* could be valid.
*/
swallow: boolean
}
export type Stop = [number, MicromarkPoint]
export type Collection = {
value: string
stops: Array<Stop>
}
/**
* @typedef {import('micromark-util-types').Event} Event
* @typedef {import('micromark-util-types').Point} Point
* @typedef {import('acorn').Comment} Comment
* @typedef {import('acorn').Node} AcornNode
* @typedef {import('acorn').Options} AcornOptions
* @typedef {import('acorn').Comment} Comment
* @typedef {import('acorn').Token} Token
* @typedef {import('acorn').Node} AcornNode
* @typedef {import('estree').Node} EstreeNode
* @typedef {import('estree').Program} Program
* @typedef {import('estree').Node} EstreeNode
* @typedef {import('micromark-util-types').Chunk} Chunk
* @typedef {import('micromark-util-types').Event} Event
* @typedef {import('micromark-util-types').Point} MicromarkPoint
* @typedef {import('unist').Point} UnistPoint
*/
/**
* @typedef Acorn
* Acorn-like interface.
* @property {import('acorn').parse} parse
* Parse a program.
* @property {import('acorn').parseExpressionAt} parseExpressionAt
* Parse an expression.
*
* @typedef {{parse: import('acorn').parse, parseExpressionAt: import('acorn').parseExpressionAt}} Acorn
* @typedef {Error & {raisedAt: number, pos: number, loc: {line: number, column: number}}} AcornError
* @typedef AcornLoc
* @property {number} line
* @property {number} column
*
* @typedef AcornErrorFields
* @property {number} raisedAt
* @property {number} pos
* @property {AcornLoc} loc
*
* @typedef {Error & AcornErrorFields} AcornError
*
* @typedef Options
* Configuration.
* @property {Acorn} acorn
* Typically `acorn`, object with `parse` and `parseExpressionAt` fields.
* @property {AcornOptions | null | undefined} [acornOptions]
* @property {Point | null | undefined} [start]
* Configuration for `acorn`.
* @property {MicromarkPoint | null | undefined} [start]
* Place where events start.
* @property {string | null | undefined} [prefix='']
* Text to place before events.
* @property {string | null | undefined} [suffix='']
* Text to place after events.
* @property {boolean | null | undefined} [expression=false]
* Whether this is a program or expression.
* @property {boolean | null | undefined} [allowEmpty=false]
* Whether an empty expression is allowed (programs are always allowed to
* be empty).
*
* @typedef Result
* Result.
* @property {Program | undefined} estree
* Program.
* @property {AcornError | undefined} error
* Error if unparseable
* @property {boolean} swallow
* Whether the error, if there is one, can be swallowed and more JavaScript
* could be valid.
*
* @typedef {[number, MicromarkPoint]} Stop
*
* @typedef Collection
* @property {string} value
* @property {Array<Stop>} stops
*/
import {visit} from 'estree-util-visit'
import {codes} from 'micromark-util-symbol/codes.js'
import {values} from 'micromark-util-symbol/values.js'
import {types} from 'micromark-util-symbol/types.js'
import {ok as assert} from 'uvu/assert'
import {visit} from 'estree-util-visit'
import {VFileMessage} from 'vfile-message'
import {location} from 'vfile-location'

@@ -33,4 +79,7 @@ /**

* @param {Array<Event>} events
* Events.
* @param {Options} options
* @returns {{estree: Program | undefined, error: Error | undefined, swallow: boolean}}
* Configuration.
* @returns {Result}
* Result.
*/

@@ -48,14 +97,7 @@ // eslint-disable-next-line complexity

const onToken = acornOptions.onToken
/** @type {Array<string>} */
const chunks = []
/** @type {Record<string, Point>} */
const lines = {}
let index = -1
let swallow = false
/** @type {AcornNode | undefined} */
let estree
/** @type {Error | undefined} */
/** @type {AcornError | undefined} */
let exception
/** @type {number} */
let startLine
/** @type {AcornOptions} */

@@ -71,24 +113,21 @@ const acornConfig = Object.assign({}, acornOptions, {

// We use `events` to detect everything, however, it could be empty.
// In that case, we need `options.start` to make sense of positional info.
if (options.start) {
startLine = options.start.line
lines[startLine] = options.start
}
const collection = collect(events, [
types.lineEnding,
// To do: these should be passed by users in parameters.
'expressionChunk', // From tests.
'mdxFlowExpressionChunk', // Flow chunk.
'mdxTextExpressionChunk', // Text chunk.
// JSX:
'mdxJsxTextTagExpressionAttributeValue',
'mdxJsxTextTagAttributeValueExpressionValue',
'mdxJsxFlowTagExpressionAttributeValue',
'mdxJsxFlowTagAttributeValueExpressionValue',
// ESM:
'mdxjsEsmData'
])
while (++index < events.length) {
const [kind, token, context] = events[index]
const source = collection.value
// Assume only void events (and `enter` followed immediately by an `exit`).
if (kind === 'exit') {
chunks.push(context.sliceSerialize(token))
setPoint(token.start)
setPoint(token.end)
}
}
const source = chunks.join('')
const value = prefix + source + suffix
const isEmptyExpression = options.expression && empty(source)
const place = location(source)

@@ -112,2 +151,4 @@ if (isEmptyExpression && !options.allowEmpty) {

error.message = String(error.message).replace(/ \(\d+:\d+\)$/, '')
// Always defined in our unist points that come from micromark.
assert(point.offset, 'expected `offset`')
error.pos = point.offset

@@ -142,7 +183,10 @@ error.loc = {line: point.line, column: point.column - 1}

const point = parseOffsetToUnistPoint(estree.end)
exception = new Error('Unexpected content after expression')
// @ts-expect-error: acorn exception.
exception.pos = point.offset
// @ts-expect-error: acorn exception.
exception.loc = {line: point.line, column: point.column - 1}
const error = /** @type {AcornError} */ (
new Error('Unexpected content after expression')
)
// Always defined in our unist points that come from micromark.
assert(point.offset, 'expected `offset`')
error.pos = point.offset
error.loc = {line: point.line, column: point.column - 1}
exception = error
estree = undefined

@@ -199,2 +243,10 @@ }

for (const token of tokens) {
// Ignore tokens that ends in prefix or start in suffix:
if (
token.end <= prefix.length ||
token.start - prefix.length >= source.length
) {
continue
}
fixPosition(token)

@@ -230,2 +282,5 @@

const pointEnd = parseOffsetToUnistPoint(nodeOrToken.end)
// Always defined in our unist points that come from micromark.
assert(pointStart.offset, 'expected `offset`')
assert(pointEnd.offset, 'expected `offset`')
nodeOrToken.start = pointStart.offset

@@ -253,3 +308,3 @@ nodeOrToken.end = pointEnd.offset

* @param {number} acornOffset
* @returns {Point}
* @returns {UnistPoint}
*/

@@ -265,25 +320,17 @@ function parseOffsetToUnistPoint(acornOffset) {

const pointInSource = place.toPoint(sourceOffset)
assert(
typeof startLine === 'number',
'expected `startLine` to be found or given '
)
const line = startLine + (pointInSource.line - 1)
assert(line in lines, 'expected line to be defined')
const column = lines[line].column + (pointInSource.column - 1)
const offset = lines[line].offset + (pointInSource.column - 1)
return /** @type {Point} */ ({line, column, offset})
}
let point = relativeToPoint(collection.stops, sourceOffset)
/** @param {Point} point */
function setPoint(point) {
// Not passed by `micromark-extension-mdxjs-esm`
/* c8 ignore next 3 */
if (!startLine || point.line < startLine) {
startLine = point.line
if (!point) {
assert(
options.start,
'empty expressions are need `options.start` being passed'
)
point = {
line: options.start.line,
column: options.start.column,
offset: options.start.offset
}
}
if (!(point.line in lines) || lines[point.line].offset > point.offset) {
lines[point.line] = point
}
return point
}

@@ -307,1 +354,134 @@ }

}
// Port from <https://github.com/wooorm/markdown-rs/blob/e692ab0/src/util/mdx_collect.rs#L15>.
/**
* @param {Array<Event>} events
* @param {Array<string>} names
* @returns {Collection}
*/
function collect(events, names) {
/** @type {Collection} */
const result = {value: '', stops: []}
let index = -1
while (++index < events.length) {
const event = events[index]
// Assume void.
if (event[0] === 'enter' && names.includes(event[1].type)) {
const chunks = event[2].sliceStream(event[1])
// Drop virtual spaces.
while (chunks.length > 0 && chunks[0] === codes.virtualSpace) {
chunks.shift()
}
const value = serializeChunks(chunks)
result.stops.push([result.value.length, event[1].start])
result.value += value
result.stops.push([result.value.length, event[1].end])
}
}
return result
}
// Port from <https://github.com/wooorm/markdown-rs/blob/e692ab0/src/util/location.rs#L91>.
/**
* Turn a relative offset into an absolute offset.
*
* @param {Array<Stop>} stops
* @param {number} relative
* @returns {UnistPoint | undefined}
*/
function relativeToPoint(stops, relative) {
let index = 0
while (index < stops.length && stops[index][0] <= relative) {
index += 1
}
// There are no points: that only occurs if there was an empty string.
if (index === 0) {
return undefined
}
const [stopRelative, stopAbsolute] = stops[index - 1]
const rest = relative - stopRelative
return {
line: stopAbsolute.line,
column: stopAbsolute.column + rest,
offset: stopAbsolute.offset + rest
}
}
// Copy from <https://github.com/micromark/micromark/blob/ce3593a/packages/micromark/dev/lib/create-tokenizer.js#L595>
// To do: expose that?
/**
* Get the string value of a slice of chunks.
*
* @param {Array<Chunk>} chunks
* @returns {string}
*/
function serializeChunks(chunks) {
let index = -1
/** @type {Array<string>} */
const result = []
/** @type {boolean | undefined} */
let atTab
while (++index < chunks.length) {
const chunk = chunks[index]
/** @type {string} */
let value
if (typeof chunk === 'string') {
value = chunk
} else
switch (chunk) {
case codes.carriageReturn: {
value = values.cr
break
}
case codes.lineFeed: {
value = values.lf
break
}
case codes.carriageReturnLineFeed: {
value = values.cr + values.lf
break
}
case codes.horizontalTab: {
value = values.ht
break
}
/* c8 ignore next 6 */
case codes.virtualSpace: {
if (atTab) continue
value = values.space
break
}
default: {
assert(typeof chunk === 'number', 'expected number')
// Currently only replacement character.
// eslint-disable-next-line unicorn/prefer-code-point
value = String.fromCharCode(chunk)
}
}
atTab = chunk === codes.horizontalTab
result.push(value)
}
return result.join('')
}

@@ -5,38 +5,101 @@ /**

* @param {Array<Event>} events
* Events.
* @param {Options} options
* @returns {{estree: Program | undefined, error: Error | undefined, swallow: boolean}}
* Configuration.
* @returns {Result}
* Result.
*/
export function eventsToAcorn(events: Array<import("micromark-util-types").Event>, options: Options): {
estree: Program | undefined;
error: Error | undefined;
swallow: boolean;
};
export type Event = import('micromark-util-types').Event;
export type Point = import('micromark-util-types').Point;
export type AcornOptions = import('acorn').Options;
export type Comment = import('acorn').Comment;
export type Token = import('acorn').Token;
export type AcornNode = import('acorn').Node;
export type Program = import('estree').Program;
export type EstreeNode = import('estree').Node;
export function eventsToAcorn(
events: Array<import('micromark-util-types').Event>,
options: Options
): Result
export type Comment = import('acorn').Comment
export type AcornNode = import('acorn').Node
export type AcornOptions = import('acorn').Options
export type Token = import('acorn').Token
export type EstreeNode = import('estree').Node
export type Program = import('estree').Program
export type Chunk = import('micromark-util-types').Chunk
export type Event = import('micromark-util-types').Event
export type MicromarkPoint = import('micromark-util-types').Point
export type UnistPoint = import('unist').Point
/**
* Acorn-like interface.
*/
export type Acorn = {
parse: typeof import("acorn").parse;
parseExpressionAt: typeof import("acorn").parseExpressionAt;
};
export type AcornError = Error & {
raisedAt: number;
pos: number;
loc: {
line: number;
column: number;
};
};
/**
* Parse a program.
*/
parse: typeof import('acorn').parse
/**
* Parse an expression.
*/
parseExpressionAt: typeof import('acorn').parseExpressionAt
}
export type AcornLoc = {
line: number
column: number
}
export type AcornErrorFields = {
raisedAt: number
pos: number
loc: AcornLoc
}
export type AcornError = Error & AcornErrorFields
/**
* Configuration.
*/
export type Options = {
acorn: Acorn;
acornOptions?: AcornOptions | null | undefined;
start?: Point | null | undefined;
prefix?: string | null | undefined;
suffix?: string | null | undefined;
expression?: boolean | null | undefined;
allowEmpty?: boolean | null | undefined;
};
/**
* Typically `acorn`, object with `parse` and `parseExpressionAt` fields.
*/
acorn: Acorn
/**
* Configuration for `acorn`.
*/
acornOptions?: AcornOptions | null | undefined
/**
* Place where events start.
*/
start?: MicromarkPoint | null | undefined
/**
* Text to place before events.
*/
prefix?: string | null | undefined
/**
* Text to place after events.
*/
suffix?: string | null | undefined
/**
* Whether this is a program or expression.
*/
expression?: boolean | null | undefined
/**
* Whether an empty expression is allowed (programs are always allowed to
* be empty).
*/
allowEmpty?: boolean | null | undefined
}
/**
* Result.
*/
export type Result = {
/**
* Program.
*/
estree: Program | undefined
/**
* Error if unparseable
*/
error: AcornError | undefined
/**
* Whether the error, if there is one, can be swallowed and more JavaScript
* could be valid.
*/
swallow: boolean
}
export type Stop = [number, MicromarkPoint]
export type Collection = {
value: string
stops: Array<Stop>
}
/**
* @typedef {import('micromark-util-types').Event} Event
* @typedef {import('micromark-util-types').Point} Point
* @typedef {import('acorn').Comment} Comment
* @typedef {import('acorn').Node} AcornNode
* @typedef {import('acorn').Options} AcornOptions
* @typedef {import('acorn').Comment} Comment
* @typedef {import('acorn').Token} Token
* @typedef {import('acorn').Node} AcornNode
* @typedef {import('estree').Node} EstreeNode
* @typedef {import('estree').Program} Program
* @typedef {import('estree').Node} EstreeNode
* @typedef {import('micromark-util-types').Chunk} Chunk
* @typedef {import('micromark-util-types').Event} Event
* @typedef {import('micromark-util-types').Point} MicromarkPoint
* @typedef {import('unist').Point} UnistPoint
*/
/**
* @typedef Acorn
* Acorn-like interface.
* @property {import('acorn').parse} parse
* Parse a program.
* @property {import('acorn').parseExpressionAt} parseExpressionAt
* Parse an expression.
*
* @typedef {{parse: import('acorn').parse, parseExpressionAt: import('acorn').parseExpressionAt}} Acorn
* @typedef {Error & {raisedAt: number, pos: number, loc: {line: number, column: number}}} AcornError
* @typedef AcornLoc
* @property {number} line
* @property {number} column
*
* @typedef AcornErrorFields
* @property {number} raisedAt
* @property {number} pos
* @property {AcornLoc} loc
*
* @typedef {Error & AcornErrorFields} AcornError
*
* @typedef Options
* Configuration.
* @property {Acorn} acorn
* Typically `acorn`, object with `parse` and `parseExpressionAt` fields.
* @property {AcornOptions | null | undefined} [acornOptions]
* @property {Point | null | undefined} [start]
* Configuration for `acorn`.
* @property {MicromarkPoint | null | undefined} [start]
* Place where events start.
* @property {string | null | undefined} [prefix='']
* Text to place before events.
* @property {string | null | undefined} [suffix='']
* Text to place after events.
* @property {boolean | null | undefined} [expression=false]
* Whether this is a program or expression.
* @property {boolean | null | undefined} [allowEmpty=false]
* Whether an empty expression is allowed (programs are always allowed to
* be empty).
*
* @typedef Result
* Result.
* @property {Program | undefined} estree
* Program.
* @property {AcornError | undefined} error
* Error if unparseable
* @property {boolean} swallow
* Whether the error, if there is one, can be swallowed and more JavaScript
* could be valid.
*
* @typedef {[number, MicromarkPoint]} Stop
*
* @typedef Collection
* @property {string} value
* @property {Array<Stop>} stops
*/
import { visit } from 'estree-util-visit';
import { VFileMessage } from 'vfile-message';
import { location } from 'vfile-location';
import {visit} from 'estree-util-visit'
import {VFileMessage} from 'vfile-message'

@@ -32,28 +75,24 @@ /**

* @param {Array<Event>} events
* Events.
* @param {Options} options
* @returns {{estree: Program | undefined, error: Error | undefined, swallow: boolean}}
* Configuration.
* @returns {Result}
* Result.
*/
// eslint-disable-next-line complexity
export function eventsToAcorn(events, options) {
const prefix = options.prefix || '';
const suffix = options.suffix || '';
const acornOptions = Object.assign({}, options.acornOptions);
const prefix = options.prefix || ''
const suffix = options.suffix || ''
const acornOptions = Object.assign({}, options.acornOptions)
/** @type {Array<Comment>} */
const comments = [];
const comments = []
/** @type {Array<Token>} */
const tokens = [];
const onComment = acornOptions.onComment;
const onToken = acornOptions.onToken;
/** @type {Array<string>} */
const chunks = [];
/** @type {Record<string, Point>} */
const lines = {};
let index = -1;
let swallow = false;
const tokens = []
const onComment = acornOptions.onComment
const onToken = acornOptions.onToken
let swallow = false
/** @type {AcornNode | undefined} */
let estree;
/** @type {Error | undefined} */
let exception;
/** @type {number} */
let startLine;
let estree
/** @type {AcornError | undefined} */
let exception
/** @type {AcornOptions} */

@@ -63,45 +102,54 @@ const acornConfig = Object.assign({}, acornOptions, {

preserveParens: true
});
})
if (onToken) {
acornConfig.onToken = tokens;
acornConfig.onToken = tokens
}
// We use `events` to detect everything, however, it could be empty.
// In that case, we need `options.start` to make sense of positional info.
if (options.start) {
startLine = options.start.line;
lines[startLine] = options.start;
}
while (++index < events.length) {
const [kind, token, context] = events[index];
// Assume only void events (and `enter` followed immediately by an `exit`).
if (kind === 'exit') {
chunks.push(context.sliceSerialize(token));
setPoint(token.start);
setPoint(token.end);
}
}
const source = chunks.join('');
const value = prefix + source + suffix;
const isEmptyExpression = options.expression && empty(source);
const place = location(source);
const collection = collect(events, [
'lineEnding',
// To do: these should be passed by users in parameters.
'expressionChunk',
// From tests.
'mdxFlowExpressionChunk',
// Flow chunk.
'mdxTextExpressionChunk',
// Text chunk.
// JSX:
'mdxJsxTextTagExpressionAttributeValue',
'mdxJsxTextTagAttributeValueExpressionValue',
'mdxJsxFlowTagExpressionAttributeValue',
'mdxJsxFlowTagAttributeValueExpressionValue',
// ESM:
'mdxjsEsmData'
])
const source = collection.value
const value = prefix + source + suffix
const isEmptyExpression = options.expression && empty(source)
if (isEmptyExpression && !options.allowEmpty) {
throw new VFileMessage('Unexpected empty expression', parseOffsetToUnistPoint(0), 'micromark-extension-mdx-expression:unexpected-empty-expression');
throw new VFileMessage(
'Unexpected empty expression',
parseOffsetToUnistPoint(0),
'micromark-extension-mdx-expression:unexpected-empty-expression'
)
}
try {
estree = options.expression && !isEmptyExpression ? options.acorn.parseExpressionAt(value, 0, acornConfig) : options.acorn.parse(value, acornConfig);
estree =
options.expression && !isEmptyExpression
? options.acorn.parseExpressionAt(value, 0, acornConfig)
: options.acorn.parse(value, acornConfig)
} catch (error_) {
const error = /** @type {AcornError} */error_;
const point = parseOffsetToUnistPoint(error.pos);
error.message = String(error.message).replace(/ \(\d+:\d+\)$/, '');
error.pos = point.offset;
const error = /** @type {AcornError} */ error_
const point = parseOffsetToUnistPoint(error.pos)
error.message = String(error.message).replace(/ \(\d+:\d+\)$/, '')
// Always defined in our unist points that come from micromark.
error.pos = point.offset
error.loc = {
line: point.line,
column: point.column - 1
};
exception = error;
swallow = error.raisedAt >= prefix.length + source.length ||
// Broken comments are raised at their start, not their end.
error.message === 'Unterminated comment';
}
exception = error
swallow =
error.raisedAt >= prefix.length + source.length ||
// Broken comments are raised at their start, not their end.
error.message === 'Unterminated comment'
}

@@ -115,22 +163,27 @@ if (estree && options.expression && !isEmptyExpression) {

// @ts-expect-error: It’s good.
body: [{
type: 'ExpressionStatement',
expression: estree,
start: 0,
end: prefix.length + source.length
}],
body: [
{
type: 'ExpressionStatement',
expression: estree,
start: 0,
end: prefix.length + source.length
}
],
sourceType: 'module',
comments: []
};
}
} else {
const point = parseOffsetToUnistPoint(estree.end);
exception = new Error('Unexpected content after expression');
// @ts-expect-error: acorn exception.
exception.pos = point.offset;
// @ts-expect-error: acorn exception.
exception.loc = {
const point = parseOffsetToUnistPoint(estree.end)
const error =
/** @type {AcornError} */
new Error('Unexpected content after expression')
// Always defined in our unist points that come from micromark.
error.pos = point.offset
error.loc = {
line: point.line,
column: point.column - 1
};
estree = undefined;
}
exception = error
estree = undefined
}

@@ -140,10 +193,11 @@ }

// @ts-expect-error: acorn *does* allow comments
estree.comments = comments;
estree.comments = comments
// @ts-expect-error: acorn looks enough like estree.
visit(estree, (esnode, field, index, parents) => {
let context = /** @type {AcornNode | Array<AcornNode>} */
parents[parents.length - 1];
let context =
/** @type {AcornNode | Array<AcornNode>} */
parents[parents.length - 1]
/** @type {string | number | null} */
let prop = field;
let prop = field

@@ -156,24 +210,38 @@ // Remove non-standard `ParenthesizedExpression`.

// @ts-expect-error: indexable.
context = context[prop];
prop = index;
context = context[prop]
prop = index
}
// @ts-expect-error: indexable.
context[prop] = esnode.expression;
context[prop] = esnode.expression
}
fixPosition(esnode);
});
fixPosition(esnode)
})
// Comment positions are fixed by `visit` because they’re in the tree.
if (Array.isArray(onComment)) {
onComment.push(...comments);
onComment.push(...comments)
} else if (typeof onComment === 'function') {
for (const comment of comments) {
onComment(comment.type === 'Block', comment.value, comment.start, comment.end, comment.loc.start, comment.loc.end);
onComment(
comment.type === 'Block',
comment.value,
comment.start,
comment.end,
comment.loc.start,
comment.loc.end
)
}
}
for (const token of tokens) {
fixPosition(token);
// Ignore tokens that ends in prefix or start in suffix:
if (
token.end <= prefix.length ||
token.start - prefix.length >= source.length
) {
continue
}
fixPosition(token)
if (Array.isArray(onToken)) {
onToken.push(token);
onToken.push(token)
} else {

@@ -183,3 +251,3 @@ // `tokens` are not added if `onToken` is not defined, so it must be a

onToken(token);
onToken(token)
}

@@ -194,3 +262,3 @@ }

swallow
};
}

@@ -204,6 +272,8 @@ /**

function fixPosition(nodeOrToken) {
const pointStart = parseOffsetToUnistPoint(nodeOrToken.start);
const pointEnd = parseOffsetToUnistPoint(nodeOrToken.end);
nodeOrToken.start = pointStart.offset;
nodeOrToken.end = pointEnd.offset;
const pointStart = parseOffsetToUnistPoint(nodeOrToken.start)
const pointEnd = parseOffsetToUnistPoint(nodeOrToken.end)
// Always defined in our unist points that come from micromark.
nodeOrToken.start = pointStart.offset
nodeOrToken.end = pointEnd.offset
nodeOrToken.loc = {

@@ -220,4 +290,4 @@ start: {

}
};
nodeOrToken.range = [nodeOrToken.start, nodeOrToken.end];
}
nodeOrToken.range = [nodeOrToken.start, nodeOrToken.end]
}

@@ -230,33 +300,20 @@

* @param {number} acornOffset
* @returns {Point}
* @returns {UnistPoint}
*/
function parseOffsetToUnistPoint(acornOffset) {
let sourceOffset = acornOffset - prefix.length;
let sourceOffset = acornOffset - prefix.length
if (sourceOffset < 0) {
sourceOffset = 0;
sourceOffset = 0
} else if (sourceOffset > source.length) {
sourceOffset = source.length;
sourceOffset = source.length
}
const pointInSource = place.toPoint(sourceOffset);
const line = startLine + (pointInSource.line - 1);
const column = lines[line].column + (pointInSource.column - 1);
const offset = lines[line].offset + (pointInSource.column - 1);
return (/** @type {Point} */{
line,
column,
offset
let point = relativeToPoint(collection.stops, sourceOffset)
if (!point) {
point = {
line: options.start.line,
column: options.start.column,
offset: options.start.offset
}
);
}
/** @param {Point} point */
function setPoint(point) {
// Not passed by `micromark-extension-mdxjs-esm`
/* c8 ignore next 3 */
if (!startLine || point.line < startLine) {
startLine = point.line;
}
if (!(point.line in lines) || lines[point.line].offset > point.offset) {
lines[point.line] = point;
}
return point
}

@@ -270,9 +327,128 @@ }

function empty(value) {
return /^\s*$/.test(value
// Multiline comments.
.replace(/\/\*[\s\S]*?\*\//g, '')
// Line comments.
// EOF instead of EOL is specifically not allowed, because that would
// mean the closing brace is on the commented-out line
.replace(/\/\/[^\r\n]*(\r\n|\n|\r)/g, ''));
}
return /^\s*$/.test(
value
// Multiline comments.
.replace(/\/\*[\s\S]*?\*\//g, '')
// Line comments.
// EOF instead of EOL is specifically not allowed, because that would
// mean the closing brace is on the commented-out line
.replace(/\/\/[^\r\n]*(\r\n|\n|\r)/g, '')
)
}
// Port from <https://github.com/wooorm/markdown-rs/blob/e692ab0/src/util/mdx_collect.rs#L15>.
/**
* @param {Array<Event>} events
* @param {Array<string>} names
* @returns {Collection}
*/
function collect(events, names) {
/** @type {Collection} */
const result = {
value: '',
stops: []
}
let index = -1
while (++index < events.length) {
const event = events[index]
// Assume void.
if (event[0] === 'enter' && names.includes(event[1].type)) {
const chunks = event[2].sliceStream(event[1])
// Drop virtual spaces.
while (chunks.length > 0 && chunks[0] === -1) {
chunks.shift()
}
const value = serializeChunks(chunks)
result.stops.push([result.value.length, event[1].start])
result.value += value
result.stops.push([result.value.length, event[1].end])
}
}
return result
}
// Port from <https://github.com/wooorm/markdown-rs/blob/e692ab0/src/util/location.rs#L91>.
/**
* Turn a relative offset into an absolute offset.
*
* @param {Array<Stop>} stops
* @param {number} relative
* @returns {UnistPoint | undefined}
*/
function relativeToPoint(stops, relative) {
let index = 0
while (index < stops.length && stops[index][0] <= relative) {
index += 1
}
// There are no points: that only occurs if there was an empty string.
if (index === 0) {
return undefined
}
const [stopRelative, stopAbsolute] = stops[index - 1]
const rest = relative - stopRelative
return {
line: stopAbsolute.line,
column: stopAbsolute.column + rest,
offset: stopAbsolute.offset + rest
}
}
// Copy from <https://github.com/micromark/micromark/blob/ce3593a/packages/micromark/dev/lib/create-tokenizer.js#L595>
// To do: expose that?
/**
* Get the string value of a slice of chunks.
*
* @param {Array<Chunk>} chunks
* @returns {string}
*/
function serializeChunks(chunks) {
let index = -1
/** @type {Array<string>} */
const result = []
/** @type {boolean | undefined} */
let atTab
while (++index < chunks.length) {
const chunk = chunks[index]
/** @type {string} */
let value
if (typeof chunk === 'string') {
value = chunk
} else
switch (chunk) {
case -5: {
value = '\r'
break
}
case -4: {
value = '\n'
break
}
case -3: {
value = '\r' + '\n'
break
}
case -2: {
value = '\t'
break
}
/* c8 ignore next 6 */
case -1: {
if (atTab) continue
value = ' '
break
}
default: {
// Currently only replacement character.
// eslint-disable-next-line unicorn/prefer-code-point
value = String.fromCharCode(chunk)
}
}
atTab = chunk === -2
result.push(value)
}
return result.join('')
}
{
"name": "micromark-util-events-to-acorn",
"version": "1.2.1",
"version": "1.2.2",
"description": "micromark utility to try and parse events w/ acorn",

@@ -44,10 +44,11 @@ "license": "MIT",

"@types/estree": "^1.0.0",
"@types/unist": "^2.0.0",
"estree-util-visit": "^1.0.0",
"micromark-util-symbol": "^1.0.0",
"micromark-util-types": "^1.0.0",
"uvu": "^0.5.0",
"vfile-location": "^4.0.0",
"vfile-message": "^3.0.0"
},
"scripts": {
"build": "tsc --build --clean && tsc && type-coverage && micromark-build"
"build": "micromark-build"
},

@@ -54,0 +55,0 @@ "xo": false,

@@ -11,3 +11,3 @@ # micromark-util-events-to-acorn

micromark utility to try and parse events w/ acorn.
[micromark][] utility to try and parse events with acorn.

@@ -20,3 +20,6 @@ ## Contents

* [`eventsToAcorn(events, options)`](#eventstoacornevents-options)
* [`Options`](#options)
* [`Result`](#result)
* [Types](#types)
* [Compatibility](#compatibility)
* [Security](#security)

@@ -29,3 +32,3 @@ * [Contribute](#contribute)

This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, 16.0+, or 18.0+), install with [npm][]:
In Node.js (version 16+), install with [npm][]:

@@ -39,3 +42,3 @@ ```sh

```js
import {mdxExpression} from 'https://esm.sh/micromark-util-events-to-acorn@1'
import {eventsToAcorn} from 'https://esm.sh/micromark-util-events-to-acorn@1'
```

@@ -47,3 +50,3 @@

<script type="module">
import {mdxExpression} from 'https://esm.sh/micromark-util-events-to-acorn@1?bundle'
import {eventsToAcorn} from 'https://esm.sh/micromark-util-events-to-acorn@1?bundle'
</script>

@@ -64,22 +67,16 @@ ```

/** @type {State} */
atClosingBrace(code) {
// …
// Gnostic mode: parse w/ acorn.
const result = eventsToAcorn(
self.events.slice(eventStart),
const result = eventsToAcorn(this.events.slice(eventStart), {
acorn,
acornOptions,
{
start: startPosition,
expression: true,
allowEmpty,
prefix: spread ? '({' : '',
suffix: spread ? '})' : ''
}
)
start: pointStart,
expression: true,
allowEmpty,
prefix: spread ? '({' : '',
suffix: spread ? '})' : ''
})
// …
}

@@ -92,6 +89,6 @@ // …

This module exports the identifier `eventsToAcorn`.
This module exports the identifier [`eventsToAcorn`][api-events-to-acorn].
There is no default export.
The export map supports the endorsed [`development` condition][condition].
The export map supports the [`development` condition][development].
Run `node --conditions development module.js` to get instrumented dev code.

@@ -106,24 +103,40 @@ Without this condition, production code is loaded.

— events
* `options.acorn` (`Acorn`, required)
— object with `acorn.parse` and
`acorn.parseExpressionAt`
* `options.acornOptions` ([`AcornOptions`][acorn-options])
— configuration for acorn
* `options.start` (`Point`, optional)
* `options` ([`Options`][api-options])
— configuration
###### Returns
Result ([`Result`][api-result]).
### `Options`
Configuration (TypeScript type).
###### Fields
* `acorn` ([`Acorn`][acorn], required)
— typically `acorn`, object with `parse` and `parseExpressionAt` fields
* `acornOptions` ([`AcornOptions`][acorn-options], optional)
— configuration for `acorn`
* `start` (`Point`, optional)
— place where events start
* `options.prefix` (`string`, default: `''`)
* `prefix` (`string`, default: `''`)
— text to place before events
* `options.suffix` (`string`, default: `''`)
* `suffix` (`string`, default: `''`)
— text to place after events
* `options.expression` (`boolean`, default: `false`)
* `expression` (`boolean`, default: `false`)
— whether this is a program or expression
* `options.allowEmpty` (`boolean`, default: `false`)
— whether an empty expression is allowed (programs are always allowed to
be empty)
* `allowEmpty` (`boolean`, default: `false`)
— whether an empty expression is allowed (programs are always allowed to be
empty)
###### Returns
### `Result`
* `estree` ([`Program?`][program])
— estree node
* `error` (`Error?`)
Result (TypeScript type).
###### Fields
* `estree` ([`Program`][program] or `undefined`)
— Program
* `error` (`Error` or `undefined`)
— error if unparseable

@@ -137,9 +150,18 @@ * `swallow` (`boolean`)

This package is fully typed with [TypeScript][].
It exports the additional types `Acorn`, `AcornOptions`, `Options`, `Point`,
and `Program`.
It exports the additional types [`Acorn`][acorn],
[`AcornOptions`][acorn-options], [`Options`][api-options], and
[`Result`][api-result].
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 16+.
Our projects sometimes work with older versions, but this is not guaranteed.
These extensions work with `micromark` version 3+.
## Security
See [`security.md`][securitymd] in [`micromark/.github`][health] for how to
submit a security report.
This package is safe.

@@ -198,10 +220,8 @@ ## Contribute

[securitymd]: https://github.com/micromark/.github/blob/HEAD/security.md
[contributing]: https://github.com/micromark/.github/blob/main/contributing.md
[contributing]: https://github.com/micromark/.github/blob/HEAD/contributing.md
[support]: https://github.com/micromark/.github/blob/main/support.md
[support]: https://github.com/micromark/.github/blob/HEAD/support.md
[coc]: https://github.com/micromark/.github/blob/main/code-of-conduct.md
[coc]: https://github.com/micromark/.github/blob/HEAD/code-of-conduct.md
[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

@@ -211,6 +231,16 @@

[condition]: https://nodejs.org/api/packages.html#packages_resolving_user_conditions
[development]: https://nodejs.org/api/packages.html#packages_resolving_user_conditions
[acorn]: https://github.com/acornjs/acorn
[acorn-options]: https://github.com/acornjs/acorn/blob/96c721dbf89d0ccc3a8c7f39e69ef2a6a3c04dfa/acorn/dist/acorn.d.ts#L16
[micromark]: https://github.com/micromark/micromark
[program]: https://github.com/estree/estree/blob/master/es2015.md#programs
[acorn-options]: https://github.com/acornjs/acorn/tree/master/acorn#interface
[api-events-to-acorn]: #eventstoacornevents-options
[api-options]: #options
[api-result]: #result
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc