beancount 0.2.5 → 0.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/build/src/classes/Node.mjs +7 -7
  2. package/build/src/classes/nodes/Blankline.mjs +2 -6
  3. package/build/src/classes/nodes/Transaction/Posting.mjs +2 -2
  4. package/build/src/genericParse.d.mts +4 -3
  5. package/build/src/genericParse.mjs +18 -19
  6. package/build/src/main.d.mts +1 -1
  7. package/build/src/parse.d.mts +1 -1
  8. package/build/src/parse.mjs +1 -1
  9. package/build/src/utils/SourceLocation.d.mts +27 -4
  10. package/build/src/utils/splitStringIntoSourceFragments.js +30 -8
  11. package/build/tsconfig.build.tsbuildinfo +1 -1
  12. package/package.json +7 -7
  13. package/build/src/classes/DateEntry.d.mts +0 -47
  14. package/build/src/classes/DateEntry.mjs +0 -60
  15. package/build/src/classes/Entry.d.mts +0 -92
  16. package/build/src/classes/Entry.mjs +0 -107
  17. package/build/src/classes/entryTypes/Balance.d.mts +0 -32
  18. package/build/src/classes/entryTypes/Balance.mjs +0 -50
  19. package/build/src/classes/entryTypes/Blankline.d.mts +0 -24
  20. package/build/src/classes/entryTypes/Blankline.mjs +0 -38
  21. package/build/src/classes/entryTypes/Close.d.mts +0 -20
  22. package/build/src/classes/entryTypes/Close.mjs +0 -31
  23. package/build/src/classes/entryTypes/Comment.d.mts +0 -25
  24. package/build/src/classes/entryTypes/Comment.mjs +0 -42
  25. package/build/src/classes/entryTypes/Commodity.d.mts +0 -20
  26. package/build/src/classes/entryTypes/Commodity.mjs +0 -31
  27. package/build/src/classes/entryTypes/Custom.d.mts +0 -23
  28. package/build/src/classes/entryTypes/Custom.mjs +0 -38
  29. package/build/src/classes/entryTypes/Document.d.mts +0 -22
  30. package/build/src/classes/entryTypes/Document.mjs +0 -34
  31. package/build/src/classes/entryTypes/Event.d.mts +0 -23
  32. package/build/src/classes/entryTypes/Event.mjs +0 -34
  33. package/build/src/classes/entryTypes/Include.d.mts +0 -20
  34. package/build/src/classes/entryTypes/Include.mjs +0 -31
  35. package/build/src/classes/entryTypes/Note.d.mts +0 -22
  36. package/build/src/classes/entryTypes/Note.mjs +0 -34
  37. package/build/src/classes/entryTypes/Open.d.mts +0 -27
  38. package/build/src/classes/entryTypes/Open.mjs +0 -66
  39. package/build/src/classes/entryTypes/Option.d.mts +0 -23
  40. package/build/src/classes/entryTypes/Option.mjs +0 -32
  41. package/build/src/classes/entryTypes/Pad.d.mts +0 -22
  42. package/build/src/classes/entryTypes/Pad.mjs +0 -33
  43. package/build/src/classes/entryTypes/Plugin.d.mts +0 -22
  44. package/build/src/classes/entryTypes/Plugin.mjs +0 -36
  45. package/build/src/classes/entryTypes/Poptag.d.mts +0 -21
  46. package/build/src/classes/entryTypes/Poptag.mjs +0 -34
  47. package/build/src/classes/entryTypes/Price.d.mts +0 -32
  48. package/build/src/classes/entryTypes/Price.mjs +0 -57
  49. package/build/src/classes/entryTypes/Pushtag.d.mts +0 -21
  50. package/build/src/classes/entryTypes/Pushtag.mjs +0 -34
  51. package/build/src/classes/entryTypes/Query.d.mts +0 -22
  52. package/build/src/classes/entryTypes/Query.mjs +0 -34
  53. package/build/src/classes/entryTypes/Transaction/Posting.d.mts +0 -59
  54. package/build/src/classes/entryTypes/Transaction/Posting.mjs +0 -97
  55. package/build/src/classes/entryTypes/Transaction/Tag.d.mts +0 -28
  56. package/build/src/classes/entryTypes/Transaction/Tag.mjs +0 -28
  57. package/build/src/classes/entryTypes/Transaction/index.d.mts +0 -70
  58. package/build/src/classes/entryTypes/Transaction/index.mjs +0 -193
  59. package/build/src/classes/entryTypes/index.d.mts +0 -19
  60. package/build/src/classes/entryTypes/index.mjs +0 -19
  61. package/build/src/entryTypeToClass.d.mts +0 -73
  62. package/build/src/entryTypeToClass.mjs +0 -37
  63. package/build/src/entryTypes.d.mts +0 -10
  64. package/build/src/entryTypes.mjs +0 -29
  65. package/build/src/utils/splitStringIntoUnparsedEntries.d.ts +0 -14
  66. package/build/src/utils/splitStringIntoUnparsedEntries.js +0 -48
@@ -1,7 +1,7 @@
1
1
  import { genericParse } from '../genericParse.mjs';
2
- import { stringAwareSplitLine } from '../utils/stringAwareSplitLine.mjs';
3
2
  import { assignWithTrimmedStrings } from '../utils/assignWithTrimmedStrings.mjs';
4
3
  import { defaultFormatOptions } from './ParseResult.mjs';
4
+ import { splitStringIntoSourceFragments } from '../utils/splitStringIntoSourceFragments.js';
5
5
  /**
6
6
  * Abstract base class for all nodes.
7
7
  *
@@ -27,8 +27,11 @@ export class Node {
27
27
  * @returns A new instance of the Node subclass
28
28
  */
29
29
  static fromString(input) {
30
- const sourceFragment = stringAwareSplitLine(input);
31
- const genericParseResult = genericParse(sourceFragment);
30
+ const sourceFragment = splitStringIntoSourceFragments(input);
31
+ if (sourceFragment.length !== 1) {
32
+ throw new Error('Wrong directive count, expected 1');
33
+ }
34
+ const genericParseResult = genericParse(sourceFragment[0]);
32
35
  const result = this.fromGenericParseResult(genericParseResult);
33
36
  if (result.type !== genericParseResult.type) {
34
37
  console.warn('Parse result type is not equal to requested object type, make sure the correct class is initiated');
@@ -70,7 +73,6 @@ export class Node {
70
73
  * @param _formatOptions - Formatting options (unused in base implementation)
71
74
  * @returns The formatted string representation of this node
72
75
  */
73
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
74
76
  toFormattedString(_formatOptions = defaultFormatOptions) {
75
77
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
76
78
  return this.toString();
@@ -103,6 +105,4 @@ export class Node {
103
105
  * @param _ctor - The constructor to validate (unused at runtime)
104
106
  * @internal
105
107
  */
106
- export function assertNodeConstructor(
107
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
108
- _ctor) { }
108
+ export function assertNodeConstructor(_ctor) { }
@@ -13,9 +13,7 @@ export class Blankline extends Node {
13
13
  * @param _genericParseResult - Unused, blank lines have no content
14
14
  * @returns A new Blankline instance
15
15
  */
16
- static fromGenericParseResult(
17
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
18
- _genericParseResult) {
16
+ static fromGenericParseResult(_genericParseResult) {
19
17
  return new Blankline({});
20
18
  }
21
19
  /** @inheritdoc */
@@ -27,9 +25,7 @@ export class Blankline extends Node {
27
25
  * @param _input - Unused, blank lines have no content
28
26
  * @returns A new Blankline instance
29
27
  */
30
- static fromString(
31
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
32
- _input) {
28
+ static fromString(_input) {
33
29
  return this.fromGenericParseResult({});
34
30
  }
35
31
  }
@@ -48,8 +48,8 @@ export class Posting {
48
48
  const accountPattern = `([^ ]*)`;
49
49
  const amountPattern = `([^A-Z;]*)`;
50
50
  const costPattern = `{(.*)}`;
51
- const pricePattern = `+(@|@@) +(?:(\\d+\\.?\\d*) ${currencyPattern})`;
52
- const amountCurrenyCostPattern = `${amountPattern}(?: +${currencyPattern})?(?: +${costPattern})?(?: ${pricePattern})?`;
51
+ const pricePattern = ` *(@@|@) *(?:([^A-Z;]+) ${currencyPattern})`;
52
+ const amountCurrenyCostPattern = `${amountPattern}(?: +${currencyPattern})?(?: +${costPattern})?(?:${pricePattern})?`;
53
53
  const commentPattern = `( *;.*)?`;
54
54
  const pattern = `^(?:${flagPattern})?${accountPattern}(?: +${amountCurrenyCostPattern})?${commentPattern}$`;
55
55
  const matches = RegExp(pattern).exec(unparsedline);
@@ -1,5 +1,6 @@
1
1
  import type { BeancountDirectiveNodeType, SyntheticNodeType } from './nodeTypeToClass.mjs';
2
2
  import { type Metadata } from './utils/parseMetadata.mjs';
3
+ import { type SourceFragmentWithLocation } from './utils/SourceLocation.mjs';
3
4
  /**
4
5
  * The basic result structure from generic parsing of a Beancount directive.
5
6
  * Contains the directive type, header line content, and any properties like comments.
@@ -58,7 +59,7 @@ export interface GenericParseResultTransaction extends Omit<GenericParseResultWi
58
59
  };
59
60
  }
60
61
  /**
61
- * Regex pattern to identify Beancount directives that start with a date.
62
+ * Regex pattern to identify Beancount directives
62
63
  * Generated from {@link NON_DATED_DIRECTIVE_TYPES} and {@link DATED_DIRECTIVE_TYPES} to ensure validation of directive types.
63
64
  */
64
65
  export declare const beanCountDirectiveRegex: RegExp;
@@ -72,7 +73,7 @@ export declare const beanCountDirectiveRegex: RegExp;
72
73
  * - Handles transaction-specific parsing (flags, body lines)
73
74
  * - Parses metadata for dated directives
74
75
  *
75
- * @param sourceFragment - Array of strings that should be parsed to a single node
76
+ * @param sourceFragment - A single source fragment
76
77
  * @returns A generic parse result object appropriate for the node type
77
78
  */
78
- export declare const genericParse: (sourceFragment: string[]) => GenericParseResult | GenericParseResultTransaction | GenericParseResultWithDate | GenericParseResultSyntheticNode;
79
+ export declare const genericParse: (sourceFragment: SourceFragmentWithLocation) => GenericParseResult | GenericParseResultTransaction | GenericParseResultWithDate | GenericParseResultSyntheticNode;
@@ -28,13 +28,13 @@ function compileDirectivePattern() {
28
28
  'txn',
29
29
  '[^ ]' /* flag */,
30
30
  ].join('|');
31
- const datePattern = `\\d{4}-\\d{2}-\\d{2}`;
32
- const datedDirectivesPattern = `${datePattern} +(?:${datedDirectivesTypePattern})`;
33
- const pattern = `^(?:${nonDatedDirectivesPattern})|(?:^${datedDirectivesPattern}) `;
31
+ const datePattern = `(?<date>\\d{4}-\\d{2}-\\d{2})`;
32
+ const datedDirectivesPattern = `${datePattern} +(?<dated_directive>${datedDirectivesTypePattern})`;
33
+ const pattern = `^(?<non_dated_directive>${nonDatedDirectivesPattern})|(?:^${datedDirectivesPattern}) `;
34
34
  return new RegExp(pattern);
35
35
  }
36
36
  /**
37
- * Regex pattern to identify Beancount directives that start with a date.
37
+ * Regex pattern to identify Beancount directives
38
38
  * Generated from {@link NON_DATED_DIRECTIVE_TYPES} and {@link DATED_DIRECTIVE_TYPES} to ensure validation of directive types.
39
39
  */
40
40
  export const beanCountDirectiveRegex = compileDirectivePattern();
@@ -48,26 +48,27 @@ export const beanCountDirectiveRegex = compileDirectivePattern();
48
48
  * - Handles transaction-specific parsing (flags, body lines)
49
49
  * - Parses metadata for dated directives
50
50
  *
51
- * @param sourceFragment - Array of strings that should be parsed to a single node
51
+ * @param sourceFragment - A single source fragment
52
52
  * @returns A generic parse result object appropriate for the node type
53
53
  */
54
54
  export const genericParse = (sourceFragment) => {
55
- const [firstLine, ...rest] = sourceFragment;
56
- if (firstLine.trim() === '') {
57
- return { type: 'blankline', header: '', props: {}, synthetic: true };
58
- }
59
- if (!beanCountDirectiveRegex.test(firstLine)) {
60
- // not a valid beancount directive, return it as a comment
55
+ const { fragment, directiveInfo } = sourceFragment;
56
+ const [firstLine, ...rest] = fragment;
57
+ if (!directiveInfo) {
58
+ // not a valid beancount directive, return it as a comment or a newline
59
+ if (firstLine.trim() === '') {
60
+ return { type: 'blankline', header: '', props: {}, synthetic: true };
61
+ }
61
62
  return {
62
63
  type: 'comment',
63
- header: sourceFragment.join('\n'),
64
+ header: fragment.join('\n'),
64
65
  props: {},
65
66
  synthetic: true,
66
67
  };
67
68
  }
68
69
  const splitFirstLine = firstLine.split(' ');
69
- if (/^\d{4}-\d{2}-\d{2}/.exec(splitFirstLine[0].trim())) {
70
- let type = splitFirstLine[1];
70
+ if (directiveInfo.date) {
71
+ let type = directiveInfo.directive;
71
72
  let flag;
72
73
  if (type.length === 1) {
73
74
  flag = type;
@@ -79,27 +80,25 @@ export const genericParse = (sourceFragment) => {
79
80
  type = 'transaction';
80
81
  }
81
82
  if (type === 'transaction') {
82
- const date = splitFirstLine[0].trim();
83
83
  const [header, ...comment] = splitFirstLine.slice(2).join(' ').split(';');
84
84
  return {
85
85
  type,
86
86
  header: header.trim(),
87
87
  body: rest.map((r) => r.trim()),
88
88
  props: {
89
- date,
89
+ date: directiveInfo.date,
90
90
  flag,
91
91
  comment: comment.length > 0 ? comment.join(';').trim() : undefined,
92
92
  },
93
93
  };
94
94
  }
95
- const date = splitFirstLine[0].trim();
96
95
  const metadata = parseMetadata(rest);
97
96
  const [header, ...comment] = splitFirstLine.slice(2).join(' ').split(';');
98
97
  return {
99
98
  type,
100
99
  header: header.trim(),
101
100
  props: {
102
- date,
101
+ date: directiveInfo.date,
103
102
  metadata,
104
103
  comment: comment.length > 0 ? comment.join(';').trim() : undefined,
105
104
  },
@@ -107,7 +106,7 @@ export const genericParse = (sourceFragment) => {
107
106
  }
108
107
  const [header, ...comment] = splitFirstLine.slice(1).join(' ').split(';');
109
108
  return {
110
- type: splitFirstLine[0].trim(),
109
+ type: directiveInfo.directive,
111
110
  header: header.trim(),
112
111
  props: {
113
112
  metadata: parseMetadata(rest),
@@ -27,7 +27,7 @@ export { deserializeNode, deserializeNodeFromString, deserializeNodes, deseriali
27
27
  export { parseFile, type ParseFileOptions, type FileSystemHelpers, } from './parseFile.mjs';
28
28
  export type { GenericParseResult, GenericParseResultWithDate, GenericParseResultTransaction, } from './genericParse.mjs';
29
29
  export type { Metadata } from './utils/parseMetadata.mjs';
30
- export { BeancountParseError, type SourceLocation, type SourceFragmentWithLocation, } from './utils/SourceLocation.mjs';
30
+ export { BeancountParseError, type DirectiveInfo, type SourceLocation, type SourceFragmentWithLocation, type SourceFragmentWithLocationDirective, type SourceFragmentWithLocationNonDirective, } from './utils/SourceLocation.mjs';
31
31
  export { ParseResult, type ParseResultObj, type FormatOptions, type CalculateCurrencyColumnOptions, } from './classes/ParseResult.mjs';
32
32
  export { Node, assertNodeConstructor } from './classes/Node.mjs';
33
33
  export { DatedNode } from './classes/DatedNode.mjs';
@@ -12,7 +12,7 @@ import { type SourceFragmentWithLocation } from './utils/SourceLocation.mjs';
12
12
  * @param fragmentWithLocation - Source fragment with location metadata
13
13
  * @returns A Node instance
14
14
  */
15
- export declare const parseSourceFragment: (fragmentWithLocation: SourceFragmentWithLocation) => Blankline | Comment | import("./classes/nodes/index.mjs").Transaction | import("./classes/nodes/Balance.mjs").Balance | import("./classes/nodes/Close.mjs").Close | import("./classes/nodes/Commodity.mjs").Commodity | import("./classes/nodes/Custom.mjs").Custom | import("./classes/nodes/Document.mjs").Document | import("./classes/nodes/Event.mjs").Event | import("./classes/nodes/Include.mjs").Include | import("./classes/nodes/Note.mjs").Note | import("./classes/nodes/Open.mjs").Open | import("./classes/nodes/Option.mjs").Option | import("./classes/nodes/Pad.mjs").Pad | import("./classes/nodes/Plugin.mjs").Plugin | import("./classes/nodes/Poptag.mjs").Poptag | import("./classes/nodes/Price.mjs").Price | import("./classes/nodes/Pushtag.mjs").Pushtag | import("./classes/nodes/Query.mjs").Query;
15
+ export declare const parseSourceFragment: (fragmentWithLocation: SourceFragmentWithLocation) => import("./classes/nodes/index.mjs").Transaction | import("./classes/nodes/Balance.mjs").Balance | Blankline | import("./classes/nodes/Close.mjs").Close | Comment | import("./classes/nodes/Commodity.mjs").Commodity | import("./classes/nodes/Custom.mjs").Custom | import("./classes/nodes/Document.mjs").Document | import("./classes/nodes/Event.mjs").Event | import("./classes/nodes/Include.mjs").Include | import("./classes/nodes/Note.mjs").Note | import("./classes/nodes/Open.mjs").Open | import("./classes/nodes/Option.mjs").Option | import("./classes/nodes/Pad.mjs").Pad | import("./classes/nodes/Plugin.mjs").Plugin | import("./classes/nodes/Price.mjs").Price | import("./classes/nodes/Poptag.mjs").Poptag | import("./classes/nodes/Pushtag.mjs").Pushtag | import("./classes/nodes/Query.mjs").Query;
16
16
  /**
17
17
  * Parses a complete Beancount file string into a ParseResult containing Node objects.
18
18
  *
@@ -19,7 +19,7 @@ export const parseSourceFragment = (fragmentWithLocation) => {
19
19
  const { fragment, location } = fragmentWithLocation;
20
20
  let nodeType;
21
21
  try {
22
- const genericParseResult = genericParse(fragment);
22
+ const genericParseResult = genericParse(fragmentWithLocation);
23
23
  const { type } = genericParseResult;
24
24
  nodeType = type;
25
25
  if (genericParseResult.synthetic) {
@@ -1,3 +1,10 @@
1
+ /**
2
+ * Information about a parsed directive
3
+ */
4
+ export interface DirectiveInfo {
5
+ date?: string;
6
+ directive: string;
7
+ }
1
8
  /**
2
9
  * Location information for a fragment of source code
3
10
  */
@@ -9,15 +16,30 @@ export interface SourceLocation {
9
16
  /** Optional file path */
10
17
  filePath?: string;
11
18
  }
12
- /**
13
- * A source fragment with associated location metadata
14
- */
15
- export interface SourceFragmentWithLocation {
19
+ interface SourceFragmentWithLocationBase {
16
20
  /** The lines of source code */
17
21
  fragment: string[];
18
22
  /** Location information */
19
23
  location: SourceLocation;
20
24
  }
25
+ /**
26
+ * A source fragment identified as a directive
27
+ */
28
+ export interface SourceFragmentWithLocationDirective extends SourceFragmentWithLocationBase {
29
+ isDirective: true;
30
+ directiveInfo: DirectiveInfo;
31
+ }
32
+ /**
33
+ * A source fragment that is not a directive (e.g. a comment)
34
+ */
35
+ export interface SourceFragmentWithLocationNonDirective extends SourceFragmentWithLocationBase {
36
+ isDirective: false;
37
+ directiveInfo: undefined;
38
+ }
39
+ /**
40
+ * A source fragment with associated location metadata
41
+ */
42
+ export type SourceFragmentWithLocation = SourceFragmentWithLocationDirective | SourceFragmentWithLocationNonDirective;
21
43
  /**
22
44
  * Custom error class for beancount parsing errors with location context
23
45
  */
@@ -70,3 +92,4 @@ export declare class BeancountParseError extends Error {
70
92
  */
71
93
  toString(): string;
72
94
  }
95
+ export {};
@@ -21,7 +21,6 @@ export const splitStringIntoSourceFragments = (source) => {
21
21
  // split up the file into source fragments with line tracking
22
22
  let inFragment = false;
23
23
  let inString = false;
24
- let isDirectiveFragment = false;
25
24
  const fragmentsWithLines = lines.reduce((acc, line, index) => {
26
25
  const lineNumber = index + 1; // 1-based line numbers
27
26
  if (!inString) {
@@ -34,34 +33,55 @@ export const splitStringIntoSourceFragments = (source) => {
34
33
  inFragment = false;
35
34
  }
36
35
  if (!inFragment) {
37
- acc.push({ lines: [], startLine: lineNumber });
38
36
  inFragment = true;
39
- isDirectiveFragment = beanCountDirectiveRegex.test(line);
37
+ const fragmentInfo = beanCountDirectiveRegex.exec(line);
38
+ if (!fragmentInfo) {
39
+ acc.push({
40
+ lines: [],
41
+ startLine: lineNumber,
42
+ isDirective: false,
43
+ directiveInfo: undefined,
44
+ });
45
+ }
46
+ else {
47
+ acc.push({
48
+ lines: [],
49
+ startLine: lineNumber,
50
+ isDirective: true,
51
+ directiveInfo: {
52
+ date: fragmentInfo.groups.date,
53
+ directive: fragmentInfo.groups.dated_directive ??
54
+ fragmentInfo.groups.non_dated_directive,
55
+ },
56
+ });
57
+ }
40
58
  }
41
59
  }
42
- acc[acc.length - 1].lines.push(line);
60
+ const currentFragment = acc[acc.length - 1];
61
+ currentFragment.lines.push(line);
43
62
  // After a blank line, ensure next line starts a new fragment
44
63
  if (!inString && line.trim() === '') {
45
64
  inFragment = false;
46
65
  }
47
66
  // odd number of ", we're in an unclosed string
48
- if (isDirectiveFragment && countChar(line, '"') % 2 === 1) {
67
+ if (currentFragment.isDirective && countChar(line, '"') % 2 === 1) {
49
68
  inString = !inString;
50
69
  }
51
70
  return acc;
52
71
  }, []);
53
- return fragmentsWithLines.map(({ lines, startLine }) => {
72
+ return fragmentsWithLines.map(({ lines, startLine, isDirective, directiveInfo }) => {
54
73
  const location = {
55
74
  startLine,
56
75
  endLine: startLine + lines.length - 1,
57
76
  };
58
77
  // Comment fragments (non-directives) may contain unbalanced quotes,
59
78
  // so skip string-aware splitting for them.
60
- const firstLine = lines[0] ?? '';
61
- if (!beanCountDirectiveRegex.test(firstLine)) {
79
+ if (!isDirective) {
62
80
  return {
63
81
  fragment: lines,
64
82
  location,
83
+ isDirective: false,
84
+ directiveInfo: undefined,
65
85
  };
66
86
  }
67
87
  try {
@@ -69,6 +89,8 @@ export const splitStringIntoSourceFragments = (source) => {
69
89
  return {
70
90
  fragment: splitLines,
71
91
  location,
92
+ isDirective: true,
93
+ directiveInfo,
72
94
  };
73
95
  }
74
96
  catch (error) {