Skip to content

Commit

Permalink
feat(util): attachComments
Browse files Browse the repository at this point in the history
Signed-off-by: Lexus Drumgold <[email protected]>
  • Loading branch information
unicornware committed Mar 5, 2024
1 parent 9ebb69b commit 926cc37
Show file tree
Hide file tree
Showing 11 changed files with 377 additions and 7 deletions.
1 change: 1 addition & 0 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,4 +90,5 @@ ignore:
profiling:
critical_files_paths:
- src/utils/compare.ts
- src/util.ts
- src/visitor.ts
2 changes: 2 additions & 0 deletions .dictionary.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@ dedupe
dedupe
deno
dessant
devlop
docast
dohm
dprint
esast
estree
fbca
gemoji
ggshield
gpgsign
hmarr
Expand Down
134 changes: 134 additions & 0 deletions __fixtures__/gemoji-shortcode.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
/**
* @file Fixtures - gemojiShortcode
* @module fixtures/gemojiShortcode
*/

import { ok as assert } from 'devlop'
import { asciiAlphanumeric } from 'micromark-util-character'
import { codes } from 'micromark-util-symbol'

/**
* Construct a union of `T` and `undefined`.
*
* @template T
* @typedef {import('@flex-development/tutils').Optional<T>} Optional
*/

/**
* @typedef {import('micromark-util-types').Code} Code
* @typedef {import('micromark-util-types').Construct} Construct
* @typedef {import('micromark-util-types').Effects} Effects
* @typedef {import('micromark-util-types').State} State
* @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext
*/

/**
* Guard whether `code` can come before a gemoji.
*
* @see {@linkcode Code}
*
* @this {TokenizeContext}
*
* @param {Code} code - Previous character code
* @return {boolean} `true` if `code` allowed before construct
*/
function previous(code) {
return code !== codes.backslash && code !== codes.colon
}

/**
* Gemoji (`:+1:`) construct.
*
* @type {Construct}
*/
export default {
/**
* Construct name.
*/
name: 'gemoji',

/**
* Guard whether `code` can come before this construct.
*
* @see {@linkcode Code}
*
* @this {TokenizeContext}
*
* @param {Code} code - Previous character code
* @return {boolean} `true` if `code` allowed before construct
*/
previous,

/**
* Set up a state machine to process character codes.
*
* @see {@linkcode Code}
* @see {@linkcode Effects}
* @see {@linkcode State}
*
* @this {TokenizeContext}
*
* @param {Effects} effects - Context object to transition state machine
* @param {State} ok - Success state function
* @param {State} nok - Error state function
* @return {State} Initial state
*/
tokenize(effects, ok, nok) {
/**
* Process the inside (`+1`) and end (`:`) of a gemoji shortcode.
*
* @param {Code} code - Character code to process
* @return {Optional<State>} Next state
*/
function inside(code) {
switch (true) {
case code === codes.colon:
effects.consume(code)
effects.exit('gemoji')
return ok
case asciiAlphanumeric(code):
case code === codes.dash:
case code === codes.plusSign:
case code === codes.underscore:
effects.consume(code)
return inside
default:
return nok(code)
}
}

/**
* Begin processing a gemoji shortcode.
*
* @param {Code} code - Character code to process
* @return {Optional<State>} Next state
*/
function begin(code) {
switch (code) {
case codes.eof:
case codes.colon:
return nok(code)
default:
effects.consume(code)
return inside
}
}

/**
* Process the start of a gemoji shortcode (`:`).
*
* @param {Code} code - Character code to process
* @return {State} Next state
*/
const start = code => {
assert(code === codes.colon, 'expected `:`')
assert(previous.call(this, this.previous), 'expected correct previous')
effects.enter('gemoji')
effects.consume(code)
return begin
}

return start
}
}
// # sourceMappingURL=gemoji-shortcode.mjs.map
7 changes: 7 additions & 0 deletions __fixtures__/micromark-util-types.d.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
declare module 'micromark-util-types' {
interface TokenTypeMap {
gemoji: 'gemoji'
}
}

export {}
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@
"chai": "5.1.0",
"cross-env": "7.0.3",
"cspell": "8.5.0",
"devlop": "1.1.0",
"dprint": "0.45.0",
"editorconfig": "2.0.0",
"esast-util-from-js": "2.0.1",
Expand All @@ -131,6 +132,9 @@
"is-ci": "3.0.1",
"jsonc-eslint-parser": "2.4.0",
"lint-staged": "15.2.2",
"micromark-util-character": "2.1.0",
"micromark-util-symbol": "2.0.0",
"micromark-util-types": "2.0.0",
"node-notifier": "10.0.1",
"prettier": "3.2.5",
"remark": "15.0.1",
Expand All @@ -142,6 +146,7 @@
"trash-cli": "5.0.0",
"ts-dedent": "2.2.0",
"typescript": "5.3.3",
"unist-util-inspect": "8.0.0",
"vfile": "6.0.1",
"vite-tsconfig-paths": "4.3.1",
"vitest": "1.3.1",
Expand Down
105 changes: 105 additions & 0 deletions src/__snapshots__/util.integration.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`integration:util > should attach comments 1`] = `
Program
body:
├─0 ImportDeclaration
│ specifiers:
│ └─0 ImportSpecifier
│ imported: Identifier<ok>
│ local: Identifier<assert>
│ source: Literal "devlop"
├─1 ImportDeclaration
│ specifiers:
│ └─0 ImportSpecifier
│ imported: Identifier<asciiAlphanumeric>
│ local: Identifier<asciiAlphanumeric>
│ source: Literal "micromark-util-character"
├─2 ImportDeclaration
│ specifiers:
│ └─0 ImportSpecifier
│ imported: Identifier<codes>
│ local: Identifier<codes>
│ source: Literal "micromark-util-symbol"
├─3 FunctionDeclaration
│ id: Identifier<previous>
│ expression: false
│ generator: false
│ async: false
│ params:
│ └─0 Identifier<code>
│ body: BlockStatement
│ body:
│ └─0 ReturnStatement
│ argument: LogicalExpression
│ left: BinaryExpression
│ left: Identifier<code>
│ operator: "!=="
│ right: MemberExpression
│ object: Identifier<codes>
│ property: Identifier<backslash>
│ computed: false
│ optional: false
│ operator: "&&"
│ right: BinaryExpression
│ left: Identifier<code>
│ operator: "!=="
│ right: MemberExpression
│ object: Identifier<codes>
│ property: Identifier<colon>
│ computed: false
│ optional: false
│ leadingComments:
│ ├─0 Block "*\\n * Construct a union of \`T\` and \`undefined\`.\\n *\\n * @template T\\n * @typedef {import('@flex-development/tutils').Optional<T>} Optional\\n "
│ ├─1 Block "*\\n * @typedef {import('micromark-util-types').Code} Code\\n * @typedef {import('micromark-util-types').Construct} Construct\\n * @typedef {import('micromark-util-types').Effects} Effects\\n * @typedef {import('micromark-util-types').State} State\\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\\n "
│ └─2 Block "*\\n * Guard whether \`code\` can come before a gemoji.\\n *\\n * @see {@linkcode Code}\\n *\\n * @this {TokenizeContext}\\n *\\n * @param {Code} code - Previous character code\\n * @return {boolean} \`true\` if \`code\` allowed before construct\\n "
│ comments:
│ ├─0 Block "*\\n * Construct a union of \`T\` and \`undefined\`.\\n *\\n * @template T\\n * @typedef {import('@flex-development/tutils').Optional<T>} Optional\\n "
│ ├─1 Block "*\\n * @typedef {import('micromark-util-types').Code} Code\\n * @typedef {import('micromark-util-types').Construct} Construct\\n * @typedef {import('micromark-util-types').Effects} Effects\\n * @typedef {import('micromark-util-types').State} State\\n * @typedef {import('micromark-util-types').TokenizeContext} TokenizeContext\\n "
│ └─2 Block "*\\n * Guard whether \`code\` can come before a gemoji.\\n *\\n * @see {@linkcode Code}\\n *\\n * @this {TokenizeContext}\\n *\\n * @param {Code} code - Previous character code\\n * @return {boolean} \`true\` if \`code\` allowed before construct\\n "
└─4 ExportDefaultDeclaration
declaration: ObjectExpression
properties:
├─0 Property
│ method: false
│ shorthand: false
│ computed: false
│ key: Identifier<name>
│ kind: "init"
│ leadingComments:
│ └─0 Block "*\\n * Construct name.\\n "
│ comments:
│ └─0 Block "*\\n * Construct name.\\n "
├─1 Property
│ method: false
│ shorthand: true
│ computed: false
│ key: Identifier<previous>
│ kind: "init"
│ leadingComments:
│ └─0 Block "*\\n * Guard whether \`code\` can come before this construct.\\n *\\n * @see {@linkcode Code}\\n *\\n * @this {TokenizeContext}\\n *\\n * @param {Code} code - Previous character code\\n * @return {boolean} \`true\` if \`code\` allowed before construct\\n "
│ comments:
│ └─0 Block "*\\n * Guard whether \`code\` can come before this construct.\\n *\\n * @see {@linkcode Code}\\n *\\n * @this {TokenizeContext}\\n *\\n * @param {Code} code - Previous character code\\n * @return {boolean} \`true\` if \`code\` allowed before construct\\n "
└─2 Property
method: true
shorthand: false
computed: false
key: Identifier<tokenize>
kind: "init"
leadingComments:
└─0 Block "*\\n * Set up a state machine to process character codes.\\n *\\n * @see {@linkcode Code}\\n * @see {@linkcode Effects}\\n * @see {@linkcode State}\\n *\\n * @this {TokenizeContext}\\n *\\n * @param {Effects} effects - Context object to transition state machine\\n * @param {State} ok - Success state function\\n * @param {State} nok - Error state function\\n * @return {State} Initial state\\n "
comments:
└─0 Block "*\\n * Set up a state machine to process character codes.\\n *\\n * @see {@linkcode Code}\\n * @see {@linkcode Effects}\\n * @see {@linkcode State}\\n *\\n * @this {TokenizeContext}\\n *\\n * @param {Effects} effects - Context object to transition state machine\\n * @param {State} ok - Success state function\\n * @param {State} nok - Error state function\\n * @return {State} Initial state\\n "
leadingComments:
└─0 Block "*\\n * Gemoji (\`:+1:\`) construct.\\n *\\n * @type {Construct}\\n "
comments:
└─0 Block "*\\n * Gemoji (\`:+1:\`) construct.\\n *\\n * @type {Construct}\\n "
sourceType: "module"
comments:
├─0 Block "*\\n * @file Fixtures - gemojiShortcode\\n * @module fixtures/gemojiShortcode\\n "
└─1 Line " # sourceMappingURL=gemoji-shortcode.mjs.map"
leadingComments:
└─0 Block "*\\n * @file Fixtures - gemojiShortcode\\n * @module fixtures/gemojiShortcode\\n "
trailingComments:
└─0 Line " # sourceMappingURL=gemoji-shortcode.mjs.map"
`;
54 changes: 54 additions & 0 deletions src/__tests__/util.integration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @file Integration Tests - util
* @module esast-util-attach-comments/tests/integration/util
*/

import { constant } from '@flex-development/tutils'
import { fromJs } from 'esast-util-from-js'
import type { Program } from 'estree'
import { visit } from 'estree-util-visit'
import { read } from 'to-vfile'
import { inspectNoColor as inspect } from 'unist-util-inspect'
import type { VFile } from 'vfile'
import type { TestContext } from 'vitest'
import testSubject from '../util'

describe('integration:util', () => {
let file: VFile
let tree: Program

beforeAll(async () => {
file = await read('__fixtures__/gemoji-shortcode.mjs')
})

beforeEach((ctx: TestContext): void => {
ctx.expect.addSnapshotSerializer({
print: (val: unknown): string => inspect(val, { showPositions: false }),
test: constant(true)
})

tree = fromJs(String(file), { module: true })
})

it('should attach comments', () => {
// Act
testSubject(tree, tree.comments)

// Expect
expect(tree).toMatchSnapshot()
})

it('should handle empty comments list', () => {
// Act
testSubject(tree)

// Expect
visit(tree, node => {
if (node.type !== 'Program') {
expect(node).not.to.have.property('comments')
expect(node).not.to.have.property('leadingComments')
expect(node).not.to.have.property('trailingComments')
}
})
})
})
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
* @module esast-util-attach-comments
*/

export {}
export { default as attachComments } from './util'
Loading

0 comments on commit 926cc37

Please sign in to comment.