From eed51d9ed8acd7d8862609dc585942fb29875366 Mon Sep 17 00:00:00 2001 From: Lexus Drumgold Date: Tue, 2 Jul 2024 19:10:10 -0400 Subject: [PATCH 1/3] feat(parsers): `PunctuatorParser` Signed-off-by: Lexus Drumgold --- .codecov.yml | 4 +- .commitlintrc.ts | 6 +- .dictionary.txt | 2 + package.json | 4 +- src/__tests__/index.e2e.spec.ts | 4 + src/index.ts | 1 + .../__snapshots__/punctuator.parser.snap | 165 ++++++ .../__tests__/punctuator.parser.spec.ts | 179 ++++++ src/parsers/abstract.parser.ts | 43 ++ src/parsers/grammar.ts | 470 ++++++++++++++++ src/parsers/index.ts | 9 + src/parsers/parser.ts | 52 ++ src/parsers/punctuator.parser.ts | 515 ++++++++++++++++++ .../__tests__/token-punctuator.spec-d.ts | 14 + src/types/index.ts | 1 + src/types/token-punctuator.ts | 17 + tsconfig.typecheck.json | 1 + .../unist-util-parsec/index.d.mts | 8 + yarn.lock | 21 +- 19 files changed, 1508 insertions(+), 8 deletions(-) create mode 100644 src/parsers/__snapshots__/punctuator.parser.snap create mode 100644 src/parsers/__tests__/punctuator.parser.spec.ts create mode 100644 src/parsers/abstract.parser.ts create mode 100644 src/parsers/grammar.ts create mode 100644 src/parsers/index.ts create mode 100644 src/parsers/parser.ts create mode 100644 src/parsers/punctuator.parser.ts create mode 100644 src/types/__tests__/token-punctuator.spec-d.ts create mode 100644 src/types/token-punctuator.ts create mode 100644 typings/@flex-development/unist-util-parsec/index.d.mts diff --git a/.codecov.yml b/.codecov.yml index c7f4709..7796572 100644 --- a/.codecov.yml +++ b/.codecov.yml @@ -90,5 +90,5 @@ ignore: profiling: critical_files_paths: - - src/location.ts - - src/reader.ts + - src/constructs/*.ts + - src/parsers/*.parser.ts diff --git a/.commitlintrc.ts b/.commitlintrc.ts index e5b3568..3be4901 100644 --- a/.commitlintrc.ts +++ b/.commitlintrc.ts @@ -18,7 +18,11 @@ import { scopes } from '@flex-development/commitlint-config' const config: UserConfig = { extends: ['@flex-development'], rules: { - 'scope-enum': [Severity.Error, 'always', scopes(['chore'])] + 'scope-enum': [Severity.Error, 'always', scopes([ + 'chore', + 'constructs', + 'parsers' + ])] } } diff --git a/.dictionary.txt b/.dictionary.txt index 81fa78a..469044c 100644 --- a/.dictionary.txt +++ b/.dictionary.txt @@ -19,6 +19,7 @@ jchen kaisugi kata keyid +kmid lcov lintstagedrc mdast @@ -30,6 +31,7 @@ pathe pkgs preid shfmt +succ tokenizes unstub vates diff --git a/package.json b/package.json index b06bc7d..1618257 100644 --- a/package.json +++ b/package.json @@ -75,8 +75,9 @@ }, "dependencies": { "@flex-development/docast": "1.0.0-alpha.17", - "@flex-development/esast": "flex-development/esast#c27d63427454b617f92dc1f01a82f3344fca7dc4", + "@flex-development/esast": "flex-development/esast#bc319cc4d5b14e2a55767bbd60ac2bfcb9ae40b2", "@flex-development/tutils": "6.0.0-alpha.25", + "@flex-development/unist-util-parsec": "flex-development/unist-util-parsec#commit=ce79a1bddd07cc6fbe97d20592e930eb77b5b756", "@flex-development/unist-util-stringify-position": "1.0.0", "@flex-development/vfile-lexer": "flex-development/vfile-lexer#commit=a4837a7d1a9c63036ed522c0e844cd7c0ed78428", "@types/mdast": "4.0.4", @@ -158,6 +159,7 @@ "yaml-eslint-parser": "1.2.3" }, "resolutions": { + "@flex-development/esast": "flex-development/esast#bc319cc4d5b14e2a55767bbd60ac2bfcb9ae40b2", "@flex-development/unist-util-types": "1.6.1", "chai": "5.1.1" }, diff --git a/src/__tests__/index.e2e.spec.ts b/src/__tests__/index.e2e.spec.ts index f0ccee9..7b8829c 100644 --- a/src/__tests__/index.e2e.spec.ts +++ b/src/__tests__/index.e2e.spec.ts @@ -8,7 +8,11 @@ import * as testSubject from '../index' describe('e2e:esast-util-from-code', () => { it('should expose public api', () => { expect(testSubject).to.have.keys([ + 'AbstractParser', + 'Grammar', 'Lexer', + 'Parser', + 'PunctuatorParser', 'Token', 'errors', 'isLineEnding', diff --git a/src/index.ts b/src/index.ts index 9f54361..ea563dd 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ export * from './enums' export type * from './interfaces' export { default as Lexer } from './lexer' +export * from './parsers' export { default as Token } from './token' export type * from './types' export * from './utils' diff --git a/src/parsers/__snapshots__/punctuator.parser.snap b/src/parsers/__snapshots__/punctuator.parser.snap new file mode 100644 index 0000000..3db0c38 --- /dev/null +++ b/src/parsers/__snapshots__/punctuator.parser.snap @@ -0,0 +1,165 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`unit:parsers/PunctuatorParser > #arrow > should parse "=>" 1`] = ` +tokens[2] +├─0 punctuator "=" (1:1-1:2, 0-1) +│ whitespace: "" +└─1 punctuator ">" (1:2-1:3, 1-2) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > #ellipsis > should parse "..." 1`] = ` +tokens[3] +├─0 punctuator "." (1:1-1:2, 0-1) +│ whitespace: "" +├─1 punctuator "." (1:2-1:3, 1-2) +│ whitespace: "" +└─2 punctuator "." (1:3-1:4, 2-3) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.ampersand 1`] = ` +punctuator "&" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.asterisk 1`] = ` +punctuator "*" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.at 1`] = ` +punctuator "@" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.backslash 1`] = ` +punctuator "\\\\" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.backtick 1`] = ` +punctuator "\`" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.bar 1`] = ` +punctuator "|" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.caret 1`] = ` +punctuator "^" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.colon 1`] = ` +punctuator ":" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.comma 1`] = ` +punctuator "," (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.dollar 1`] = ` +keyid "$" (1:1-1:2, 0-1) + whitespace: "" + private: false +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.dot 1`] = ` +punctuator "." (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.equal 1`] = ` +punctuator "=" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.exclamation 1`] = ` +punctuator "!" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.gt 1`] = ` +punctuator ">" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.hash 1`] = ` +punctuator "#" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.leftBrace 1`] = ` +punctuator "{" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.leftBracket 1`] = ` +punctuator "[" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.leftParen 1`] = ` +punctuator "(" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.lt 1`] = ` +punctuator "<" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.minus 1`] = ` +punctuator "-" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.percent 1`] = ` +punctuator "%" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.plus 1`] = ` +punctuator "+" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.question 1`] = ` +punctuator "?" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.rightBrace 1`] = ` +punctuator "}" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.rightBracket 1`] = ` +punctuator "]" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.rightParen 1`] = ` +punctuator ")" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.semicolon 1`] = ` +punctuator ";" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.slash 1`] = ` +punctuator "/" (1:1-1:2, 0-1) + whitespace: "" +`; + +exports[`unit:parsers/PunctuatorParser > chars > should parse chars.tilde 1`] = ` +punctuator "~" (1:1-1:2, 0-1) + whitespace: "" +`; diff --git a/src/parsers/__tests__/punctuator.parser.spec.ts b/src/parsers/__tests__/punctuator.parser.spec.ts new file mode 100644 index 0000000..b2592f2 --- /dev/null +++ b/src/parsers/__tests__/punctuator.parser.spec.ts @@ -0,0 +1,179 @@ +/** + * @file Unit Tests - PunctuatorParser + * @module esast-util-from-code/parsers/tests/punctuator/unit + */ + +import { tt } from '#src/enums' +import type Token from '#src/token' +import type { PunctuatorToken } from '#src/types' +import { constant, omit } from '@flex-development/tutils' +import { u } from '@flex-development/unist-util-builder' +import { inspectNoColor } from '@flex-development/unist-util-inspect' +import { + eof, + kmid, + ParseError, + tok, + type Runner as P, + type ParserOutput, + type RepNResult, + type TokenType as TT +} from '@flex-development/unist-util-parsec' +import { chars } from '@flex-development/vfile-lexer' +import type { Node } from 'unist' +import type { TestContext } from 'vitest' +import TestSubject from '../parser' + +describe('unit:parsers/PunctuatorParser', () => { + type PunctuatorResult = PunctuatorToken | RepNResult + + let parse: (x: P, token: Token) => void + let test: (x: P) => P + + beforeAll(() => { + test = x => kmid(tok(tt.sof), x, tok(tt.eof)) + + parse = ( + x: P, + token: Token + ): void => { + /** + * Parser output. + * + * @const {ParserOutput} output + */ + const output: ParserOutput = test(x).parse(token) + + expect(() => eof(output)).not.to.throw + expect(output.successful).to.be.true + expect(output.candidate).to.have.property('head', token) + expect(output.candidate).to.have.property('next', undefined) + expect(output.candidate).to.have.property('result') + expect(output.candidate?.result).toMatchSnapshot() + } + }) + + beforeEach((context: TestContext) => { + context.expect.addSnapshotSerializer({ + // @ts-expect-error no support for known values (2322). + print(value: Token | Token[]): string { + return Array.isArray(value) + ? inspectNoColor(u('tokens', value.map(node))) + : inspectNoColor(node(value)) + + /** + * Convert `token` to a node. + * + * @param {Token} token - Token to convert + * @return {Node} `token` as node + */ + function node(token: Token): Node { + return u(token.type, { + ...omit(token, ['end', 'next', 'previous', 'start']), + position: { end: token.end, start: token.start } + }) + } + }, + test: constant(true) + }) + }) + + describe('#arrow', () => { + it('should fail on invalid whitespace', () => { + // Arrange + const file: string = chars.equal + chars.space + chars.gt + const subject: TestSubject = new TestSubject(file) + + // Act + const output = test(subject.arrow).parse(subject.tokenizer.head) + + // Expect + expect(output.successful).to.be.false + expect(output.error).to.be.instanceof(ParseError) + }) + + it('should parse "=>"', () => { + // Arrange + const subject: TestSubject = new TestSubject(chars.equal + chars.gt) + + // Act + Expect + parse(subject.arrow, subject.tokenizer.head) + }) + }) + + describe('#ellipsis', () => { + it('should fail on invalid whitespace (sample #1)', () => { + // Arrange + const file: string = chars.dot + chars.lf + chars.dot.repeat(2) + const subject: TestSubject = new TestSubject(file) + + // Act + const output = test(subject.arrow).parse(subject.tokenizer.head) + + // Expect + expect(output.successful).to.be.false + expect(output.error).to.be.instanceof(ParseError) + }) + + it('should fail on invalid whitespace (sample #2)', () => { + // Arrange + const file: string = chars.dot.repeat(2) + chars.lf + chars.dot + const subject: TestSubject = new TestSubject(file) + + // Act + const output = test(subject.arrow).parse(subject.tokenizer.head) + + // Expect + expect(output.successful).to.be.false + expect(output.error).to.be.instanceof(ParseError) + }) + + it('should parse "..."', () => { + // Arrange + const subject: TestSubject = new TestSubject(chars.dot.repeat(3)) + + // Act + Expect + parse(subject.ellipsis, subject.tokenizer.head) + }) + }) + + describe('chars', () => { + it.each([ + 'ampersand', + 'asterisk', + 'at', + 'backslash', + 'backtick', + 'bar', + 'caret', + 'colon', + 'comma', + 'dollar', + 'dot', + 'equal', + 'exclamation', + 'gt', + 'hash', + 'leftBrace', + 'leftBracket', + 'leftParen', + 'lt', + 'minus', + 'percent', + 'plus', + 'question', + 'rightBrace', + 'rightBracket', + 'rightParen', + 'semicolon', + 'slash', + 'tilde' + ] as const)('should parse chars.%s', key => { + // Arrange + const subject: TestSubject = new TestSubject(chars[key]) + + // Act + Expect + parse(subject[key], subject.tokenizer.head) + }) + }) +}) diff --git a/src/parsers/abstract.parser.ts b/src/parsers/abstract.parser.ts new file mode 100644 index 0000000..dae1432 --- /dev/null +++ b/src/parsers/abstract.parser.ts @@ -0,0 +1,43 @@ +/** + * @file Parsers - AbstractParser + * @module esast-util-from-code/parsers/AbstractParser + */ + +import type { Options } from '#src/interfaces' +import Grammar from './grammar' + +/** + * Abstract source file parser. + * + * @see {@linkcode Grammar} + * + * @class + * @abstract + * @extends {Grammar} + */ +abstract class AbstractParser extends Grammar { + /** + * Parser options. + * + * @see {@linkcode Options} + * + * @protected + * @instance + * @member {Options} options + */ + protected options: Options + + /** + * Create a new file parser. + * + * @see {@linkcode Options} + * + * @param {(Options| null)?} [options] - Parser options + */ + constructor(options?: Options | null) { + super() + this.options = options ?? {} + } +} + +export default AbstractParser diff --git a/src/parsers/grammar.ts b/src/parsers/grammar.ts new file mode 100644 index 0000000..6cae0b5 --- /dev/null +++ b/src/parsers/grammar.ts @@ -0,0 +1,470 @@ +/** + * @file Parsers - Grammar + * @module esast-util-from-code/parsers/Grammar + */ + +import type { PunctuatorToken } from '#src/types' +import type { + Runner as P, + RepNResult, + TokenType as TT +} from '@flex-development/unist-util-parsec' +import { chars } from '@flex-development/vfile-lexer' + +/** + * Abstract parser grammar map. + * + * @class + * @abstract + */ +abstract class Grammar { + /** + * Get the ampersand (`&`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.ampersand} + * + * @public + * @abstract + * @instance + * + * @return {P} Ampersand parser + */ + public abstract get ampersand(): P + + /** + * Get the arrow (`=>`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode RepNResult} + * + * @public + * @abstract + * @instance + * + * @return {P>} Arrow parser + */ + public abstract get arrow(): P> + + /** + * Get the asterisk (`*`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.asterisk} + * + * @public + * @abstract + * @instance + * + * @return {P} Asterisk parser + */ + public abstract get asterisk(): P + + /** + * Get the at symbol (`@`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.at} + * + * @public + * @abstract + * @instance + * + * @return {P} At symbol parser + */ + public abstract get at(): P + + /** + * Get the backslash (`\`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.backslash} + * + * @public + * @abstract + * @instance + * + * @return {P} Backslash parser + */ + public abstract get backslash(): P + + /** + * Get the backtick parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.backtick} + * + * @public + * @abstract + * @instance + * + * @return {P} Backtick parser + */ + public abstract get backtick(): P + + /** + * Get the bar (`|`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.bar} + * + * @public + * @abstract + * @instance + * + * @return {P} Bar parser + */ + public abstract get bar(): P + + /** + * Get the caret (`^`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.caret} + * + * @public + * @abstract + * @instance + * + * @return {P} Caret parser + */ + public abstract get caret(): P + + /** + * Get the colon (`:`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.colon} + * + * @public + * @abstract + * @instance + * + * @return {P} Colon parser + */ + public abstract get colon(): P + + /** + * Get the comma (`,`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.comma} + * + * @public + * @abstract + * @instance + * + * @return {P} Comma parser + */ + public abstract get comma(): P + + /** + * Get the dollar sign (`$`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.dollar} + * + * @public + * @abstract + * @instance + * + * @return {P} Dollar sign parser + */ + public abstract get dollar(): P + + /** + * Get the dot (`.`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.dot} + * + * @public + * @abstract + * @instance + * + * @return {P} Dot parser + */ + public abstract get dot(): P + + /** + * Get the ellipsis (`...`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode RepNResult} + * + * @public + * @abstract + * @instance + * + * @return {P>} Ellipsis parser + */ + public abstract get ellipsis(): P> + + /** + * Get the equal sign (`=`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.equal} + * + * @public + * @abstract + * @instance + * + * @return {P} Equal sign parser + */ + public abstract get equal(): P + + /** + * Get the exclamation mark (`!`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.exclamation} + * + * @public + * @abstract + * @instance + * + * @return {P} Exclamation mark parser + */ + public abstract get exclamation(): P + + /** + * Get the greater than symbol (`>`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.gt} + * + * @public + * @abstract + * @instance + * + * @return {P} Greater than symbol parser + */ + public abstract get gt(): P + + /** + * Get the hash symbol (`#`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.hash} + * + * @public + * @abstract + * @instance + * + * @return {P} Hash symbol parser + */ + public abstract get hash(): P + + /** + * Get the left brace (`{`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.leftBrace} + * + * @public + * @abstract + * @instance + * + * @return {P} Left brace parser + */ + public abstract get leftBrace(): P + + /** + * Get the left bracket (`[`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.leftBracket} + * + * @public + * @abstract + * @instance + * + * @return {P} Left bracket parser + */ + public abstract get leftBracket(): P + + /** + * Get the left parenthesis (`(`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.leftParen} + * + * @public + * @abstract + * @instance + * + * @return {P} Left parenthesis parser + */ + public abstract get leftParen(): P + + /** + * Get the less than symbol (`<`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.lt} + * + * @public + * @abstract + * @instance + * + * @return {P} Less than symbol parser + */ + public abstract get lt(): P + + /** + * Get the minus sign (`-`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.minus} + * + * @public + * @abstract + * @instance + * + * @return {P} Minus sign parser + */ + public abstract get minus(): P + + /** + * Get the percent sign (`%`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.percent} + * + * @public + * @abstract + * @instance + * + * @return {P} Percent sign parser + */ + public abstract get percent(): P + + /** + * Get the plus sign (`+`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.plus} + * + * @public + * @abstract + * @instance + * + * @return {P} Plus sign parser + */ + public abstract get plus(): P + + /** + * Get the question mark (`?`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.question} + * + * @public + * @abstract + * @instance + * + * @return {P} Question mark parser + */ + public abstract get question(): P + + /** + * Get the right brace (`}`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.rightBrace} + * + * @public + * @abstract + * @instance + * + * @return {P} Right brace parser + */ + public abstract get rightBrace(): P + + /** + * Get the right bracket (`]`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.rightBracket} + * + * @public + * @abstract + * @instance + * + * @return {P} Right bracket parser + */ + public abstract get rightBracket(): P + + /** + * Get the right parenthesis (`)`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.rightParen} + * + * @public + * @abstract + * @instance + * + * @return {P} Right parenthesis parser + */ + public abstract get rightParen(): P + + /** + * Get the semicolon (`;`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.semicolon} + * + * @public + * @abstract + * @instance + * + * @return {P} Semicolon parser + */ + public abstract get semicolon(): P + + /** + * Get the forward slash (`/`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.slash} + * + * @public + * @abstract + * @instance + * + * @return {P} Forward slash parser + */ + public abstract get slash(): P + + /** + * Get the tilde (`~`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.tilde} + * + * @public + * @abstract + * @instance + * + * @return {P} Tilde parser + */ + public abstract get tilde(): P + + /** + * Forbid leading whitespace. + * + * @public + * @abstract + * @instance + * + * @template {any} R - Parse candidate result + * + * @param {P} x - Parser to apply + * @return {typeof x} No whitespace parser + */ + public abstract nw(x: P): typeof x +} + +export default Grammar diff --git a/src/parsers/index.ts b/src/parsers/index.ts new file mode 100644 index 0000000..81dbc6d --- /dev/null +++ b/src/parsers/index.ts @@ -0,0 +1,9 @@ +/** + * @file Entry Point - Parsers + * @module esast-util-from-code/parsers + */ + +export { default as AbstractParser } from './abstract.parser' +export { default as Grammar } from './grammar' +export { default as Parser } from './parser' +export { default as PunctuatorParser } from './punctuator.parser' diff --git a/src/parsers/parser.ts b/src/parsers/parser.ts new file mode 100644 index 0000000..4d3d522 --- /dev/null +++ b/src/parsers/parser.ts @@ -0,0 +1,52 @@ +/** + * @file Parsers - Parser + * @module esast-util-from-code/parsers/parser + */ + +import type { Options } from '#src/interfaces' +import Lexer from '#src/lexer' +import type { Value, VFile } from 'vfile' +import type Grammar from './grammar' +import PunctuatorParser from './punctuator.parser' + +/** + * ECMAScript parser. + * + * @see {@linkcode Grammar} + * @see {@linkcode PunctuatorParser} + * + * @class + * @extends {PunctuatorParser} + * @implements {Grammar} + */ +class Parser extends PunctuatorParser implements Grammar { + /** + * Source file tokenizer. + * + * @see {@linkcode Lexer} + * + * @public + * @readonly + * @instance + * @member {Readonly} tokenizer + */ + public readonly tokenizer: Readonly + + /** + * Create a new ECMAScript parser. + * + * @see {@linkcode Options} + * @see {@linkcode VFile} + * @see {@linkcode Value} + * + * @param {(Value | VFile | null)?} file - File to parse + * @param {(Options | null)?} [options] - Parser options + */ + constructor(file?: Value | VFile | null, options?: Options | null) { + super(options) + this.tokenizer = Object.freeze(new Lexer(file, this.options)) + Object.defineProperties(this, { tokenizer: { writable: false } }) + } +} + +export default Parser diff --git a/src/parsers/punctuator.parser.ts b/src/parsers/punctuator.parser.ts new file mode 100644 index 0000000..3a9556a --- /dev/null +++ b/src/parsers/punctuator.parser.ts @@ -0,0 +1,515 @@ +/** + * @file Parsers - PunctuatorParser + * @module esast-util-from-code/parsers/punctuator + */ + +import type { tt } from '#src/enums' +import type { PunctuatorToken } from '#src/types' +import { + combine, + condition, + fail, + seq, + succ, + val, + type Runner as P, + type RepNResult, + type TokenType as TT +} from '@flex-development/unist-util-parsec' +import { chars } from '@flex-development/vfile-lexer' +import AbstractParser from './abstract.parser' + +/** + * Punctuator parser. + * + * @see {@linkcode AbstractParser} + * + * @class + * @abstract + * @extends {AbstractParser} + */ +abstract class PunctuatorParser extends AbstractParser { + /** + * Get the ampersand (`&`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.ampersand} + * + * @public + * @instance + * + * @return {P} Ampersand parser + */ + public get ampersand(): P { + return val(chars.ampersand) + } + + /** + * Get the arrow (`=>`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode RepNResult} + * + * @public + * @instance + * + * @return {P>} Arrow parser + */ + public get arrow(): P> { + return seq(this.equal, this.nw(this.gt)) + } + + /** + * Get the asterisk (`*`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.asterisk} + * + * @public + * @instance + * + * @return {P} Asterisk parser + */ + public get asterisk(): P { + return val(chars.asterisk) + } + + /** + * Get the at symbol (`@`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.at} + * + * @public + * @instance + * + * @return {P} At symbol parser + */ + public get at(): P { + return val(chars.at) + } + + /** + * Get the backslash (`\`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.backslash} + * + * @public + * @instance + * + * @return {P} Backslash parser + */ + public get backslash(): P { + return val(chars.backslash) + } + + /** + * Get the backtick parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.backtick} + * + * @public + * @instance + * + * @return {P} Backtick parser + */ + public get backtick(): P { + return val(chars.backtick) + } + + /** + * Get the bar (`|`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.bar} + * + * @public + * @instance + * + * @return {P} Bar parser + */ + public get bar(): P { + return val(chars.bar) + } + + /** + * Get the caret (`^`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.caret} + * + * @public + * @instance + * + * @return {P} Caret parser + */ + public get caret(): P { + return val(chars.caret) + } + + /** + * Get the colon (`:`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.colon} + * + * @public + * @instance + * + * @return {P} Colon parser + */ + public get colon(): P { + return val(chars.colon) + } + + /** + * Get the comma (`,`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.comma} + * + * @public + * @instance + * + * @return {P} Comma parser + */ + public get comma(): P { + return val(chars.comma) + } + + /** + * Get the dollar sign (`$`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.dollar} + * + * @public + * @instance + * + * @return {P} Dollar sign parser + */ + public get dollar(): P { + return val(chars.dollar) + } + + /** + * Get the dot (`.`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.dot} + * + * @public + * @instance + * + * @return {P} Dot parser + */ + public get dot(): P { + return val(chars.dot) + } + + /** + * Get the ellipsis (`...`) parser. + * + * @see {@linkcode RepNResult} + * @see {@linkcode PunctuatorToken} + * + * @public + * @instance + * + * @return {P>} Ellipsis parser + */ + public get ellipsis(): P> { + return seq(this.dot, this.nw(this.dot), this.nw(this.dot)) + } + + /** + * Get the equal sign (`=`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.equal} + * + * @public + * @instance + * + * @return {P} Equal sign parser + */ + public get equal(): P { + return val(chars.equal) + } + + /** + * Get the exclamation mark (`!`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.exclamation} + * + * @public + * @instance + * + * @return {P} Exclamation mark parser + */ + public get exclamation(): P { + return val(chars.exclamation) + } + + /** + * Get the right angle (`>`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.gt} + * + * @public + * @instance + * + * @return {P} Right angle parser + */ + public get gt(): P { + return val(chars.gt) + } + + /** + * Get the hash symbol (`#`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.hash} + * + * @public + * @instance + * + * @return {P} Hash symbol parser + */ + public get hash(): P { + return val(chars.hash) + } + + /** + * Get the left brace (`{`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.leftBrace} + * + * @public + * @instance + * + * @return {P} Left brace parser + */ + public get leftBrace(): P { + return val(chars.leftBrace) + } + + /** + * Get the left bracket (`[`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.leftBracket} + * + * @public + * @instance + * + * @return {P} Left bracket parser + */ + public get leftBracket(): P { + return val(chars.leftBracket) + } + + /** + * Get the left parenthesis (`(`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.leftParen} + * + * @public + * @instance + * + * @return {P} Left parenthesis parser + */ + public get leftParen(): P { + return val(chars.leftParen) + } + + /** + * Get the left angle (`<`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.lt} + * + * @public + * @instance + * + * @return {P} Left angle parser + */ + public get lt(): P { + return val(chars.lt) + } + + /** + * Get the minus sign (`-`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.minus} + * + * @public + * @instance + * + * @return {P} Minus sign parser + */ + public get minus(): P { + return val(chars.minus) + } + + /** + * Get the percent sign (`%`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.percent} + * + * @public + * @instance + * + * @return {P} Percent sign parser + */ + public get percent(): P { + return val(chars.percent) + } + + /** + * Get the plus sign (`+`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.plus} + * + * @public + * @instance + * + * @return {P} Plus sign parser + */ + public get plus(): P { + return val(chars.plus) + } + + /** + * Get the question mark (`?`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.question} + * + * @public + * @instance + * + * @return {P} Question mark parser + */ + public get question(): P { + return val(chars.question) + } + + /** + * Get the right brace (`}`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.rightBrace} + * + * @public + * @instance + * + * @return {P} Right brace parser + */ + public get rightBrace(): P { + return val(chars.rightBrace) + } + + /** + * Get the right bracket (`]`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.rightBracket} + * + * @public + * @instance + * + * @return {P} Right bracket parser + */ + public get rightBracket(): P { + return val(chars.rightBracket) + } + + /** + * Get the right parenthesis (`)`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.rightParen} + * + * @public + * @instance + * + * @return {P} Right parenthesis parser + */ + public get rightParen(): P { + return val(chars.rightParen) + } + + /** + * Get the semicolon (`;`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.semicolon} + * + * @public + * @instance + * + * @return {P} Semicolon parser + */ + public get semicolon(): P { + return val(chars.semicolon) + } + + /** + * Get the forward slash (`/`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.slash} + * + * @public + * @instance + * + * @return {P} Forward slash parser + */ + public get slash(): P { + return val(chars.slash) + } + + /** + * Get the tilde (`~`) parser. + * + * @see {@linkcode PunctuatorToken} + * @see {@linkcode chars.tilde} + * + * @public + * @instance + * + * @return {P} Tilde parser + */ + public get tilde(): P { + return val(chars.tilde) + } + + /** + * Forbid leading whitespace. + * + * @public + * @instance + * + * @template {any} R - Parse candidate result + * + * @param {P} x - Parser to apply + * @return {typeof x} No whitespace parser + */ + public nw(x: P): typeof x { + return combine(x, (result, [head]) => { + return condition(head?.whitespace, fail(), succ(result)) + }) + } +} + +export default PunctuatorParser diff --git a/src/types/__tests__/token-punctuator.spec-d.ts b/src/types/__tests__/token-punctuator.spec-d.ts new file mode 100644 index 0000000..50738e5 --- /dev/null +++ b/src/types/__tests__/token-punctuator.spec-d.ts @@ -0,0 +1,14 @@ +/** + * @file Type Tests - PunctuatorToken + * @module esast-util-from-code/types/tests/unit-d/PunctuatorToken + */ + +import type { tt } from '#src/enums' +import type Token from '#src/token' +import type TestSubject from '../token-punctuator' + +describe('unit-d:types/PunctuatorToken', () => { + it('should equal Token', () => { + expectTypeOf().toEqualTypeOf>() + }) +}) diff --git a/src/types/index.ts b/src/types/index.ts index 72fd7eb..105de79 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -5,4 +5,5 @@ export type { Code, Offset } from '@flex-development/vfile-lexer' export type { default as TokenInfo } from './token-info' +export type { default as PunctuatorToken } from './token-punctuator' export type { default as TokenValue } from './token-value' diff --git a/src/types/token-punctuator.ts b/src/types/token-punctuator.ts new file mode 100644 index 0000000..a2c3916 --- /dev/null +++ b/src/types/token-punctuator.ts @@ -0,0 +1,17 @@ +/** + * @file Type Aliases - PunctuatorToken + * @module esast-util-from-code/types/PunctuatorToken + */ + +import type { tt } from '#src/enums' +import type Token from '#src/token' + +/** + * Token representing a punctuator. + * + * @see {@linkcode Token} + * @see {@linkcode tt.punctuator} + */ +type PunctuatorToken = Token + +export type { PunctuatorToken as default } diff --git a/tsconfig.typecheck.json b/tsconfig.typecheck.json index 08de84f..8e39caa 100644 --- a/tsconfig.typecheck.json +++ b/tsconfig.typecheck.json @@ -3,6 +3,7 @@ "extends": "./tsconfig.json", "files": [ "typings/@faker-js/faker/global.d.ts", + "typings/@flex-development/unist-util-parsec/index.d.mts", "typings/@flex-development/vfile-lexer/index.d.mts", "vitest-env.d.ts" ], diff --git a/typings/@flex-development/unist-util-parsec/index.d.mts b/typings/@flex-development/unist-util-parsec/index.d.mts new file mode 100644 index 0000000..1f2e088 --- /dev/null +++ b/typings/@flex-development/unist-util-parsec/index.d.mts @@ -0,0 +1,8 @@ +import type {} from '@flex-development/unist-util-parsec' +import type * as lexer from '@flex-development/vfile-lexer' + +declare module '@flex-development/unist-util-parsec' { + interface Token extends lexer.Token {} + + interface TokenTypeMap extends lexer.TokenTypeMap {} +} diff --git a/yarn.lock b/yarn.lock index 5f70bf5..6c61794 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1532,7 +1532,7 @@ __metadata: "@flex-development/commitlint-config": "npm:1.0.1" "@flex-development/decorator-regex": "npm:2.0.0" "@flex-development/docast": "npm:1.0.0-alpha.17" - "@flex-development/esast": "flex-development/esast#c27d63427454b617f92dc1f01a82f3344fca7dc4" + "@flex-development/esast": "flex-development/esast#bc319cc4d5b14e2a55767bbd60ac2bfcb9ae40b2" "@flex-development/esm-types": "npm:2.0.0" "@flex-development/grease": "npm:3.0.0-alpha.9" "@flex-development/mkbuild": "npm:1.0.0-alpha.23" @@ -1541,6 +1541,7 @@ __metadata: "@flex-development/tutils": "npm:6.0.0-alpha.25" "@flex-development/unist-util-builder": "npm:2.0.0" "@flex-development/unist-util-inspect": "npm:1.0.0" + "@flex-development/unist-util-parsec": "flex-development/unist-util-parsec#commit=ce79a1bddd07cc6fbe97d20592e930eb77b5b756" "@flex-development/unist-util-stringify-position": "npm:1.0.0" "@flex-development/unist-util-types": "npm:1.6.1" "@flex-development/vfile-lexer": "flex-development/vfile-lexer#commit=a4837a7d1a9c63036ed522c0e844cd7c0ed78428" @@ -1607,15 +1608,15 @@ __metadata: languageName: unknown linkType: soft -"@flex-development/esast@flex-development/esast#c27d63427454b617f92dc1f01a82f3344fca7dc4": +"@flex-development/esast@flex-development/esast#bc319cc4d5b14e2a55767bbd60ac2bfcb9ae40b2": version: 1.0.0-alpha.5 - resolution: "@flex-development/esast@https://github.com/flex-development/esast.git#commit=c27d63427454b617f92dc1f01a82f3344fca7dc4" + resolution: "@flex-development/esast@https://github.com/flex-development/esast.git#commit=bc319cc4d5b14e2a55767bbd60ac2bfcb9ae40b2" dependencies: "@flex-development/docast": "npm:1.0.0-alpha.17" "@flex-development/unist-util-types": "npm:1.4.0" "@types/mdast": "npm:4.0.3" "@types/unist": "npm:3.0.2" - checksum: 10/25ae4c7e99376f7f48e0992ffa333effbb886f8d248c89a69f0790e6cc97e9a186a72ea9379a4fdbde2dc5f764a45a8f94b00e2bc84562a2321d4b3eddaf8294 + checksum: 10/698c10da68ad0a25cea8e51ec8a93650e2ac47738da881c10296795a15b3704473fddc1f60fe3ab4810dd7bf08c61918461237ef8c10902b98eea832622ead36 languageName: node linkType: hard @@ -1969,6 +1970,18 @@ __metadata: languageName: node linkType: hard +"@flex-development/unist-util-parsec@flex-development/unist-util-parsec#commit=ce79a1bddd07cc6fbe97d20592e930eb77b5b756": + version: 0.0.0 + resolution: "@flex-development/unist-util-parsec@https://github.com/flex-development/unist-util-parsec.git#commit=ce79a1bddd07cc6fbe97d20592e930eb77b5b756" + dependencies: + "@flex-development/unist-util-builder": "npm:2.0.0" + "@flex-development/unist-util-types": "npm:1.6.1" + "@types/unist": "npm:3.0.2" + devlop: "npm:1.1.0" + checksum: 10/aa5aebac9e7f87e0ef2e2eda61144068645ef7b8ba47802668ff47e4fd2643622d4e4f985294e92dee275c320f97a124136be647065641a0128f495465d3c562 + languageName: node + linkType: hard + "@flex-development/unist-util-stringify-position@npm:1.0.0": version: 1.0.0 resolution: "@flex-development/unist-util-stringify-position@npm:1.0.0::__archiveUrl=https%3A%2F%2Fnpm.pkg.github.com%2Fdownload%2F%40flex-development%2Funist-util-stringify-position%2F1.0.0%2Fb2a5cbc97f53cee6e215f15a908a78109e543b48" From 1acf6b53be8d82c3a5e6a2fb302fb5bf7f10508b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:11:29 +0000 Subject: [PATCH 2/3] build(deps-dev): Bump globals from 15.6.0 to 15.8.0 Bumps [globals](https://github.com/sindresorhus/globals) from 15.6.0 to 15.8.0. - [Release notes](https://github.com/sindresorhus/globals/releases) - [Commits](https://github.com/sindresorhus/globals/compare/v15.6.0...v15.8.0) --- updated-dependencies: - dependency-name: globals dependency-type: direct:development update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- package.json | 2 +- yarn.lock | 17 ++++++++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 1618257..337322f 100644 --- a/package.json +++ b/package.json @@ -135,7 +135,7 @@ "eslint-plugin-promise": "6.2.0", "eslint-plugin-unicorn": "54.0.0", "eslint-plugin-yml": "1.14.0", - "globals": "15.6.0", + "globals": "15.8.0", "growl": "1.10.5", "husky": "9.0.11", "is-ci": "3.0.1", diff --git a/yarn.lock b/yarn.lock index 6c61794..58c7a57 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1581,7 +1581,7 @@ __metadata: eslint-plugin-promise: "npm:6.2.0" eslint-plugin-unicorn: "npm:54.0.0" eslint-plugin-yml: "npm:1.14.0" - globals: "npm:15.6.0" + globals: "npm:15.8.0" growl: "npm:1.10.5" husky: "npm:9.0.11" is-ci: "npm:3.0.1" @@ -5919,10 +5919,10 @@ __metadata: languageName: node linkType: hard -"globals@npm:15.6.0, globals@npm:^15.0.0": - version: 15.6.0 - resolution: "globals@npm:15.6.0" - checksum: 10/9b522b1eff444acaf331b0da7f072d5ef75b1cde02d89b3656039252f01b6bcda7a9ea42128e8644117c0ede9116249b1bcf830458b7dbe19fafd4de916a3dc0 +"globals@npm:15.8.0": + version: 15.8.0 + resolution: "globals@npm:15.8.0" + checksum: 10/fbca69cc1084c28a5155fea06224f6f0fcd9a371d8b8057db9c1851adc4bf2aad8016dd0db4e161e0ad1da0953e4286b90c826ff9ef5dbfb864493f2bba806df languageName: node linkType: hard @@ -5933,6 +5933,13 @@ __metadata: languageName: node linkType: hard +"globals@npm:^15.0.0": + version: 15.6.0 + resolution: "globals@npm:15.6.0" + checksum: 10/9b522b1eff444acaf331b0da7f072d5ef75b1cde02d89b3656039252f01b6bcda7a9ea42128e8644117c0ede9116249b1bcf830458b7dbe19fafd4de916a3dc0 + languageName: node + linkType: hard + "globalthis@npm:^1.0.3": version: 1.0.3 resolution: "globalthis@npm:1.0.3" From d448e25ec7caefeb9b04a2e7fcbb0badac17788b Mon Sep 17 00:00:00 2001 From: "flex-development[bot]" <148604919+flex-development[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 23:11:51 +0000 Subject: [PATCH 3/3] build(yarn): [dependabot skip] deduplicate dependencies for @dependabot Signed-off-by: flex-development[bot] <148604919+flex-development[bot]@users.noreply.github.com> --- yarn.lock | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/yarn.lock b/yarn.lock index 58c7a57..68f053e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5919,7 +5919,7 @@ __metadata: languageName: node linkType: hard -"globals@npm:15.8.0": +"globals@npm:15.8.0, globals@npm:^15.0.0": version: 15.8.0 resolution: "globals@npm:15.8.0" checksum: 10/fbca69cc1084c28a5155fea06224f6f0fcd9a371d8b8057db9c1851adc4bf2aad8016dd0db4e161e0ad1da0953e4286b90c826ff9ef5dbfb864493f2bba806df @@ -5933,13 +5933,6 @@ __metadata: languageName: node linkType: hard -"globals@npm:^15.0.0": - version: 15.6.0 - resolution: "globals@npm:15.6.0" - checksum: 10/9b522b1eff444acaf331b0da7f072d5ef75b1cde02d89b3656039252f01b6bcda7a9ea42128e8644117c0ede9116249b1bcf830458b7dbe19fafd4de916a3dc0 - languageName: node - linkType: hard - "globalthis@npm:^1.0.3": version: 1.0.3 resolution: "globalthis@npm:1.0.3"