beancount 0.0.31 → 0.2.0

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 (71) hide show
  1. package/README.md +51 -22
  2. package/build/src/benchmark.mjs +1 -1
  3. package/build/src/classes/DatedNode.d.mts +40 -0
  4. package/build/src/classes/DatedNode.mjs +61 -0
  5. package/build/src/classes/Node.d.mts +92 -0
  6. package/build/src/classes/Node.mjs +107 -0
  7. package/build/src/classes/ParseResult.d.mts +75 -75
  8. package/build/src/classes/ParseResult.mjs +96 -98
  9. package/build/src/classes/nodes/Balance.d.mts +32 -0
  10. package/build/src/classes/nodes/Balance.mjs +50 -0
  11. package/build/src/classes/nodes/Blankline.d.mts +23 -0
  12. package/build/src/classes/nodes/Blankline.mjs +37 -0
  13. package/build/src/classes/nodes/Close.d.mts +20 -0
  14. package/build/src/classes/nodes/Close.mjs +31 -0
  15. package/build/src/classes/nodes/Comment.d.mts +25 -0
  16. package/build/src/classes/nodes/Comment.mjs +42 -0
  17. package/build/src/classes/nodes/Commodity.d.mts +20 -0
  18. package/build/src/classes/nodes/Commodity.mjs +31 -0
  19. package/build/src/classes/nodes/Custom.d.mts +23 -0
  20. package/build/src/classes/nodes/Custom.mjs +38 -0
  21. package/build/src/classes/nodes/Document.d.mts +22 -0
  22. package/build/src/classes/nodes/Document.mjs +34 -0
  23. package/build/src/classes/nodes/Event.d.mts +23 -0
  24. package/build/src/classes/nodes/Event.mjs +34 -0
  25. package/build/src/classes/nodes/Include.d.mts +20 -0
  26. package/build/src/classes/nodes/Include.mjs +31 -0
  27. package/build/src/classes/nodes/Note.d.mts +22 -0
  28. package/build/src/classes/nodes/Note.mjs +34 -0
  29. package/build/src/classes/nodes/Open.d.mts +27 -0
  30. package/build/src/classes/nodes/Open.mjs +66 -0
  31. package/build/src/classes/nodes/Option.d.mts +23 -0
  32. package/build/src/classes/nodes/Option.mjs +32 -0
  33. package/build/src/classes/nodes/Pad.d.mts +22 -0
  34. package/build/src/classes/nodes/Pad.mjs +33 -0
  35. package/build/src/classes/nodes/Plugin.d.mts +22 -0
  36. package/build/src/classes/nodes/Plugin.mjs +36 -0
  37. package/build/src/classes/nodes/Poptag.d.mts +21 -0
  38. package/build/src/classes/nodes/Poptag.mjs +34 -0
  39. package/build/src/classes/nodes/Price.d.mts +32 -0
  40. package/build/src/classes/nodes/Price.mjs +57 -0
  41. package/build/src/classes/nodes/Pushtag.d.mts +21 -0
  42. package/build/src/classes/nodes/Pushtag.mjs +34 -0
  43. package/build/src/classes/nodes/Query.d.mts +22 -0
  44. package/build/src/classes/nodes/Query.mjs +34 -0
  45. package/build/src/classes/nodes/Transaction/Posting.d.mts +59 -0
  46. package/build/src/classes/nodes/Transaction/Posting.mjs +97 -0
  47. package/build/src/classes/nodes/Transaction/Tag.d.mts +28 -0
  48. package/build/src/classes/nodes/Transaction/Tag.mjs +28 -0
  49. package/build/src/classes/nodes/Transaction/index.d.mts +70 -0
  50. package/build/src/classes/nodes/Transaction/index.mjs +193 -0
  51. package/build/src/classes/nodes/index.d.mts +19 -0
  52. package/build/src/classes/nodes/index.mjs +19 -0
  53. package/build/src/cli.mjs +4 -4
  54. package/build/src/deserialize.d.mts +54 -54
  55. package/build/src/deserialize.mjs +89 -89
  56. package/build/src/directiveTypes.d.mts +10 -0
  57. package/build/src/directiveTypes.mjs +29 -0
  58. package/build/src/genericParse.d.mts +27 -20
  59. package/build/src/genericParse.mjs +30 -30
  60. package/build/src/main.d.mts +30 -31
  61. package/build/src/main.mjs +30 -30
  62. package/build/src/nodeTypeToClass.d.mts +81 -0
  63. package/build/src/nodeTypeToClass.mjs +37 -0
  64. package/build/src/parse.d.mts +16 -16
  65. package/build/src/parse.mjs +37 -37
  66. package/build/src/parseFile.d.mts +2 -2
  67. package/build/src/parseFile.mjs +11 -11
  68. package/build/src/utils/splitStringIntoSourceFragments.d.ts +14 -0
  69. package/build/src/utils/splitStringIntoSourceFragments.js +48 -0
  70. package/build/tsconfig.build.tsbuildinfo +1 -1
  71. package/package.json +7 -7
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * @remarks
5
5
  * The primary function you'll use is {@link parse}, which parses a complete Beancount file
6
- * and returns a {@link ParseResult} containing all parsed entries.
6
+ * and returns a {@link ParseResult} containing parsed nodes.
7
7
  *
8
8
  * @example
9
9
  * ```typescript
@@ -17,42 +17,42 @@
17
17
  * `
18
18
  *
19
19
  * const result = parse(beancountContent)
20
- * console.log(result.entries) // Array of parsed Entry objects
20
+ * console.log(result.nodes) // Array of parsed Node objects
21
21
  * ```
22
22
  *
23
23
  * @module
24
24
  */
25
25
  // Primary parsing functionality
26
- export { parse, parseEntry } from './parse.mjs';
27
- export { deserializeEntry, deserializeEntryFromString, deserializeEntries, deserializeEntriesFromString, } from './deserialize.mjs';
26
+ export { parse, parseSourceFragment } from './parse.mjs';
27
+ export { deserializeNode, deserializeNodeFromString, deserializeNodes, deserializeNodesFromString, } from './deserialize.mjs';
28
28
  export { parseFile, } from './parseFile.mjs';
29
29
  // Core classes
30
30
  export { ParseResult, } from './classes/ParseResult.mjs';
31
- export { Entry, assertEntryConstructor } from './classes/Entry.mjs';
32
- export { DateEntry } from './classes/DateEntry.mjs';
31
+ export { Node, assertNodeConstructor } from './classes/Node.mjs';
32
+ export { DatedNode } from './classes/DatedNode.mjs';
33
33
  export { Value } from './classes/Value.mjs';
34
- // Entry type classes
35
- export { Balance } from './classes/entryTypes/Balance.mjs';
36
- export { Blankline } from './classes/entryTypes/Blankline.mjs';
37
- export { Close } from './classes/entryTypes/Close.mjs';
38
- export { Comment } from './classes/entryTypes/Comment.mjs';
39
- export { Commodity } from './classes/entryTypes/Commodity.mjs';
40
- export { Custom } from './classes/entryTypes/Custom.mjs';
41
- export { Document } from './classes/entryTypes/Document.mjs';
42
- export { Event } from './classes/entryTypes/Event.mjs';
43
- export { Include } from './classes/entryTypes/Include.mjs';
44
- export { Note } from './classes/entryTypes/Note.mjs';
45
- export { Open } from './classes/entryTypes/Open.mjs';
46
- export { Option } from './classes/entryTypes/Option.mjs';
47
- export { Pad } from './classes/entryTypes/Pad.mjs';
48
- export { Plugin } from './classes/entryTypes/Plugin.mjs';
49
- export { Poptag } from './classes/entryTypes/Poptag.mjs';
50
- export { Price } from './classes/entryTypes/Price.mjs';
51
- export { Pushtag } from './classes/entryTypes/Pushtag.mjs';
52
- export { Query } from './classes/entryTypes/Query.mjs';
53
- export { Transaction, } from './classes/entryTypes/Transaction/index.mjs';
34
+ // Node classes
35
+ export { Balance } from './classes/nodes/Balance.mjs';
36
+ export { Blankline } from './classes/nodes/Blankline.mjs';
37
+ export { Close } from './classes/nodes/Close.mjs';
38
+ export { Comment } from './classes/nodes/Comment.mjs';
39
+ export { Commodity } from './classes/nodes/Commodity.mjs';
40
+ export { Custom } from './classes/nodes/Custom.mjs';
41
+ export { Document } from './classes/nodes/Document.mjs';
42
+ export { Event } from './classes/nodes/Event.mjs';
43
+ export { Include } from './classes/nodes/Include.mjs';
44
+ export { Note } from './classes/nodes/Note.mjs';
45
+ export { Open } from './classes/nodes/Open.mjs';
46
+ export { Option } from './classes/nodes/Option.mjs';
47
+ export { Pad } from './classes/nodes/Pad.mjs';
48
+ export { Plugin } from './classes/nodes/Plugin.mjs';
49
+ export { Poptag } from './classes/nodes/Poptag.mjs';
50
+ export { Price } from './classes/nodes/Price.mjs';
51
+ export { Pushtag } from './classes/nodes/Pushtag.mjs';
52
+ export { Query } from './classes/nodes/Query.mjs';
53
+ export { Transaction, } from './classes/nodes/Transaction/index.mjs';
54
54
  // Transaction sub-components
55
- export { Posting } from './classes/entryTypes/Transaction/Posting.mjs';
56
- export { Tag } from './classes/entryTypes/Transaction/Tag.mjs';
57
- export { beancountEntryToClass, entryTypeToClass } from './entryTypeToClass.mjs';
58
- export { DATED_ENTRY_TYPES, NON_DATED_ENTRY_TYPES } from './entryTypes.mjs';
55
+ export { Posting } from './classes/nodes/Transaction/Posting.mjs';
56
+ export { Tag } from './classes/nodes/Transaction/Tag.mjs';
57
+ export { beancountDirectiveNodeTypeToClass, nodeTypeToClass, } from './nodeTypeToClass.mjs';
58
+ export { DATED_DIRECTIVE_TYPES, NON_DATED_DIRECTIVE_TYPES, } from './directiveTypes.mjs';
@@ -0,0 +1,81 @@
1
+ import { Transaction, Balance, Close, Comment, Commodity, Custom, Document, Event, Include, Note, Open, Option, Pad, Plugin, Poptag, Price, Pushtag, Query, Blankline } from './classes/nodes/index.mjs';
2
+ import type { DATED_DIRECTIVE_TYPES, NON_DATED_DIRECTIVE_TYPES } from './directiveTypes.mjs';
3
+ /**
4
+ * Union type of all node types that correspond to a Beancount directive that includes a date field.
5
+ * Derived from {@link DATED_DIRECTIVE_TYPES}.
6
+ */
7
+ export type DatedDirectiveNodeType = (typeof DATED_DIRECTIVE_TYPES)[number];
8
+ /**
9
+ * Union type of all node types that correspond to a Beancount directive type that does NOT include
10
+ * a date field.
11
+ * Derived from {@link NON_DATED_DIRECTIVE_TYPES}.
12
+ */
13
+ export type NonDatedDirectiveNodeType = (typeof NON_DATED_DIRECTIVE_TYPES)[number];
14
+ /**
15
+ * Union type of all node type that corrospond to Beancount directives.
16
+ * Directive types derived from https://beancount.github.io/docs/beancount_language_syntax.html#directives-1
17
+ * Node can have additional synthetic types, see {@link SyntheticNodeType}
18
+ *
19
+ * Derived from {@link DATED_DIRECTIVE_TYPES} and {@link NON_DATED_DIRECTIVE_TYPES}.
20
+ */
21
+ export type BeancountDirectiveNodeType = DatedDirectiveNodeType | NonDatedDirectiveNodeType;
22
+ /**
23
+ * Union type of synthetic node types that are not part of the official Beancount directive syntax.
24
+ *
25
+ * These node types are created during parsing to preserve the complete structure of a Beancount file,
26
+ * enabling lossless round-trip parsing
27
+ */
28
+ export type SyntheticNodeType = 'comment' | 'blankline';
29
+ /**
30
+ * Union type of all valid node type names, that being the beancount directive types and the additional
31
+ * synthetic node types of comment and blankline
32
+ */
33
+ export type NodeType = BeancountDirectiveNodeType | SyntheticNodeType;
34
+ /**
35
+ * Mapping of Beancount directive node type names to their corresponding class constructors.
36
+ * @internal
37
+ */
38
+ export declare const beancountDirectiveNodeTypeToClass: {
39
+ transaction: typeof Transaction;
40
+ balance: typeof Balance;
41
+ close: typeof Close;
42
+ commodity: typeof Commodity;
43
+ custom: typeof Custom;
44
+ document: typeof Document;
45
+ event: typeof Event;
46
+ include: typeof Include;
47
+ note: typeof Note;
48
+ open: typeof Open;
49
+ option: typeof Option;
50
+ pad: typeof Pad;
51
+ plugin: typeof Plugin;
52
+ poptag: typeof Poptag;
53
+ price: typeof Price;
54
+ pushtag: typeof Pushtag;
55
+ query: typeof Query;
56
+ };
57
+ /**
58
+ * Mapping of all node type names (including synthetic) to their corresponding class constructors.
59
+ * @internal
60
+ */
61
+ export declare const nodeTypeToClass: {
62
+ comment: typeof Comment;
63
+ blankline: typeof Blankline;
64
+ transaction: typeof Transaction;
65
+ balance: typeof Balance;
66
+ close: typeof Close;
67
+ commodity: typeof Commodity;
68
+ custom: typeof Custom;
69
+ document: typeof Document;
70
+ event: typeof Event;
71
+ include: typeof Include;
72
+ note: typeof Note;
73
+ open: typeof Open;
74
+ option: typeof Option;
75
+ pad: typeof Pad;
76
+ plugin: typeof Plugin;
77
+ poptag: typeof Poptag;
78
+ price: typeof Price;
79
+ pushtag: typeof Pushtag;
80
+ query: typeof Query;
81
+ };
@@ -0,0 +1,37 @@
1
+ import { Transaction, Balance, Close, Comment, Commodity, Custom, Document, Event, Include, Note, Open, Option, Pad, Plugin, Poptag, Price, Pushtag, Query, Blankline, } from './classes/nodes/index.mjs';
2
+ /**
3
+ * Mapping of Beancount directive node type names to their corresponding class constructors.
4
+ * @internal
5
+ */
6
+ export const beancountDirectiveNodeTypeToClass = {
7
+ transaction: Transaction,
8
+ balance: Balance,
9
+ close: Close,
10
+ commodity: Commodity,
11
+ custom: Custom,
12
+ document: Document,
13
+ event: Event,
14
+ include: Include,
15
+ note: Note,
16
+ open: Open,
17
+ option: Option,
18
+ pad: Pad,
19
+ plugin: Plugin,
20
+ poptag: Poptag,
21
+ price: Price,
22
+ pushtag: Pushtag,
23
+ query: Query,
24
+ };
25
+ // Compile-time assertion: beancountDirectiveNodeTypeToClass must have all BeancountDirectiveNodeType keys
26
+ beancountDirectiveNodeTypeToClass;
27
+ /**
28
+ * Mapping of all node type names (including synthetic) to their corresponding class constructors.
29
+ * @internal
30
+ */
31
+ export const nodeTypeToClass = {
32
+ ...beancountDirectiveNodeTypeToClass,
33
+ comment: Comment,
34
+ blankline: Blankline,
35
+ };
36
+ // Compile-time assertion: nodeTypeToClass must have all NodeType keys
37
+ nodeTypeToClass;
@@ -1,29 +1,29 @@
1
1
  import { ParseResult } from './classes/ParseResult.mjs';
2
- import { Comment, Blankline } from './classes/entryTypes/index.mjs';
2
+ import { Comment, Blankline } from './classes/nodes/index.mjs';
3
3
  /**
4
- * Parses a single unparsed entry into its corresponding Entry class instance.
4
+ * Parses a single source fragment into its corresponding Node class instance.
5
5
  *
6
6
  * This function:
7
- * - Performs generic parsing to determine entry type
8
- * - Instantiates the appropriate Entry subclass
7
+ * - Performs generic parsing to determine node type
8
+ * - Instantiates the appropriate Node subclass
9
9
  * - Handles special cases for comments and blank lines
10
10
  *
11
- * @param unparsedEntry - Array of string tokens representing a single entry
12
- * @returns An Entry instance
11
+ * @param sourceFragment - Array of string tokens that should be parsed to a single node
12
+ * @returns A Node instance
13
13
  */
14
- export declare const parseEntry: (unparsedEntry: string[]) => import("./classes/entryTypes/index.mjs").Transaction | import("./classes/entryTypes/Balance.mjs").Balance | import("./classes/entryTypes/Close.mjs").Close | import("./classes/entryTypes/Commodity.mjs").Commodity | import("./classes/entryTypes/Custom.mjs").Custom | import("./classes/entryTypes/Document.mjs").Document | import("./classes/entryTypes/Event.mjs").Event | import("./classes/entryTypes/Include.mjs").Include | import("./classes/entryTypes/Note.mjs").Note | import("./classes/entryTypes/Open.mjs").Open | import("./classes/entryTypes/Option.mjs").Option | import("./classes/entryTypes/Pad.mjs").Pad | import("./classes/entryTypes/Plugin.mjs").Plugin | import("./classes/entryTypes/Poptag.mjs").Poptag | import("./classes/entryTypes/Price.mjs").Price | import("./classes/entryTypes/Pushtag.mjs").Pushtag | import("./classes/entryTypes/Query.mjs").Query | Comment | Blankline;
14
+ export declare const parseSourceFragment: (sourceFragment: string[]) => 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 | Comment | Blankline;
15
15
  /**
16
- * Parses a complete Beancount file string into a ParseResult containing Entry objects.
16
+ * Parses a complete Beancount file string into a ParseResult containing Node objects.
17
17
  *
18
18
  * This is the main entry point for parsing Beancount files. It handles:
19
- * - Splitting the input into individual entries
20
- * - Parsing each entry into its appropriate type
21
- * - Managing the tag stack for pushtag/poptag directives
19
+ * - Splitting the source into source fragment
20
+ * - Parsing each source fragment into its appropriate node
21
+ * - Managing the tag stack for pushtag/poptag nodes
22
22
  * - Applying tags from the stack to transactions
23
23
  *
24
24
  * @remarks
25
25
  * This is the primary function you'll use from this library. It takes a Beancount file
26
- * as a string and returns a structured ParseResult object containing all parsed entries.
26
+ * as a string and returns a structured ParseResult object containing the resulting parsed nodes.
27
27
  *
28
28
  * @example
29
29
  * Basic usage:
@@ -38,10 +38,10 @@ export declare const parseEntry: (unparsedEntry: string[]) => import("./classes/
38
38
  * `
39
39
  *
40
40
  * const result = parse(content)
41
- * // result.entries contains parsed Entry objects
41
+ * // result.nodes contains parsed Node objects
42
42
  * ```
43
43
  *
44
- * @param input - The complete Beancount file content as a string
45
- * @returns A ParseResult instance containing all parsed entries
44
+ * @param source - The complete Beancount file content as a string
45
+ * @returns A ParseResult instance containing all parsed nodes
46
46
  */
47
- export declare const parse: (input: string) => ParseResult;
47
+ export declare const parse: (source: string) => ParseResult;
@@ -1,23 +1,23 @@
1
1
  import { ParseResult } from './classes/ParseResult.mjs';
2
- import { Comment, Blankline } from './classes/entryTypes/index.mjs';
2
+ import { Comment, Blankline } from './classes/nodes/index.mjs';
3
3
  import { genericParse, } from './genericParse.mjs';
4
- import { beancountEntryToClass, } from './entryTypeToClass.mjs';
5
- import { splitStringIntoUnparsedEntries } from './utils/splitStringIntoUnparsedEntries.js';
4
+ import { beancountDirectiveNodeTypeToClass, } from './nodeTypeToClass.mjs';
5
+ import { splitStringIntoSourceFragments } from './utils/splitStringIntoSourceFragments.js';
6
6
  /**
7
- * Parses a single unparsed entry into its corresponding Entry class instance.
7
+ * Parses a single source fragment into its corresponding Node class instance.
8
8
  *
9
9
  * This function:
10
- * - Performs generic parsing to determine entry type
11
- * - Instantiates the appropriate Entry subclass
10
+ * - Performs generic parsing to determine node type
11
+ * - Instantiates the appropriate Node subclass
12
12
  * - Handles special cases for comments and blank lines
13
13
  *
14
- * @param unparsedEntry - Array of string tokens representing a single entry
15
- * @returns An Entry instance
14
+ * @param sourceFragment - Array of string tokens that should be parsed to a single node
15
+ * @returns A Node instance
16
16
  */
17
- export const parseEntry = (unparsedEntry) => {
18
- const genericParseResult = genericParse(unparsedEntry);
17
+ export const parseSourceFragment = (sourceFragment) => {
18
+ const genericParseResult = genericParse(sourceFragment);
19
19
  const { type } = genericParseResult;
20
- if (genericParseResult.fake) {
20
+ if (genericParseResult.synthetic) {
21
21
  if (type === 'blankline') {
22
22
  return Blankline.fromGenericParseResult(genericParseResult);
23
23
  }
@@ -25,26 +25,26 @@ export const parseEntry = (unparsedEntry) => {
25
25
  return Comment.fromGenericParseResult(genericParseResult);
26
26
  }
27
27
  }
28
- const EntryClass = beancountEntryToClass[type];
29
- if (EntryClass) {
30
- return EntryClass.fromGenericParseResult(genericParseResult);
28
+ const NodeClass = beancountDirectiveNodeTypeToClass[type];
29
+ if (NodeClass) {
30
+ return NodeClass.fromGenericParseResult(genericParseResult);
31
31
  }
32
32
  else {
33
- throw Error(`Could not parse ${unparsedEntry.toString()}`);
33
+ throw Error(`Could not parse ${sourceFragment.toString()}`);
34
34
  }
35
35
  };
36
36
  /**
37
- * Parses a complete Beancount file string into a ParseResult containing Entry objects.
37
+ * Parses a complete Beancount file string into a ParseResult containing Node objects.
38
38
  *
39
39
  * This is the main entry point for parsing Beancount files. It handles:
40
- * - Splitting the input into individual entries
41
- * - Parsing each entry into its appropriate type
42
- * - Managing the tag stack for pushtag/poptag directives
40
+ * - Splitting the source into source fragment
41
+ * - Parsing each source fragment into its appropriate node
42
+ * - Managing the tag stack for pushtag/poptag nodes
43
43
  * - Applying tags from the stack to transactions
44
44
  *
45
45
  * @remarks
46
46
  * This is the primary function you'll use from this library. It takes a Beancount file
47
- * as a string and returns a structured ParseResult object containing all parsed entries.
47
+ * as a string and returns a structured ParseResult object containing the resulting parsed nodes.
48
48
  *
49
49
  * @example
50
50
  * Basic usage:
@@ -59,25 +59,25 @@ export const parseEntry = (unparsedEntry) => {
59
59
  * `
60
60
  *
61
61
  * const result = parse(content)
62
- * // result.entries contains parsed Entry objects
62
+ * // result.nodes contains parsed Node objects
63
63
  * ```
64
64
  *
65
- * @param input - The complete Beancount file content as a string
66
- * @returns A ParseResult instance containing all parsed entries
65
+ * @param source - The complete Beancount file content as a string
66
+ * @returns A ParseResult instance containing all parsed nodes
67
67
  */
68
- export const parse = (input) => {
69
- const unparsedEntries = splitStringIntoUnparsedEntries(input);
70
- const parsedEntries = [];
68
+ export const parse = (source) => {
69
+ const sourceFragments = splitStringIntoSourceFragments(source);
70
+ const nodes = [];
71
71
  const tagStack = [];
72
- for (const unparsedEntry of unparsedEntries) {
73
- const parsedEntry = parseEntry(unparsedEntry);
74
- if (parsedEntry) {
75
- if (parsedEntry.type === 'pushtag') {
76
- tagStack.push(parsedEntry.tag);
72
+ for (const sourceFragment of sourceFragments) {
73
+ const node = parseSourceFragment(sourceFragment);
74
+ if (node) {
75
+ if (node.type === 'pushtag') {
76
+ tagStack.push(node.tag);
77
77
  }
78
- else if (parsedEntry.type === 'poptag') {
78
+ else if (node.type === 'poptag') {
79
79
  // Find and remove the most recent matching tag from the stack
80
- const tagToRemove = parsedEntry.tag.content;
80
+ const tagToRemove = node.tag.content;
81
81
  for (let i = tagStack.length - 1; i >= 0; i--) {
82
82
  if (tagStack[i].content === tagToRemove) {
83
83
  tagStack.splice(i, 1);
@@ -85,11 +85,11 @@ export const parse = (input) => {
85
85
  }
86
86
  }
87
87
  }
88
- if (parsedEntry.type === 'transaction') {
89
- parsedEntry.tags.push(...tagStack);
88
+ if (node.type === 'transaction') {
89
+ node.tags.push(...tagStack);
90
90
  }
91
- parsedEntries.push(parsedEntry);
91
+ nodes.push(node);
92
92
  }
93
93
  }
94
- return new ParseResult(parsedEntries);
94
+ return new ParseResult(nodes);
95
95
  };
@@ -18,7 +18,7 @@ export type { FileSystemHelpers };
18
18
  *
19
19
  * @param filepath - Path to the Beancount file to parse
20
20
  * @param options - Parsing options
21
- * @returns A ParseResult containing all parsed entries
21
+ * @returns A ParseResult containing all parsed nodes
22
22
  *
23
23
  * @example
24
24
  * Basic usage (Node.js):
@@ -32,7 +32,7 @@ export type { FileSystemHelpers };
32
32
  * With recursive parsing of includes (Node.js):
33
33
  * ```typescript
34
34
  * const result = await parseFile('/path/to/main.beancount', { recurse: true })
35
- * // All entries from included files are merged into the result
35
+ * // All nodes from included files are merged into the result
36
36
  * ```
37
37
  *
38
38
  * @example
@@ -6,7 +6,7 @@ import { getDefaultFileSystemHelpers, } from './fileSystemHelpers.mjs';
6
6
  *
7
7
  * @param filepath - Path to the Beancount file to parse
8
8
  * @param options - Parsing options
9
- * @returns A ParseResult containing all parsed entries
9
+ * @returns A ParseResult containing all parsed nodes
10
10
  *
11
11
  * @example
12
12
  * Basic usage (Node.js):
@@ -20,7 +20,7 @@ import { getDefaultFileSystemHelpers, } from './fileSystemHelpers.mjs';
20
20
  * With recursive parsing of includes (Node.js):
21
21
  * ```typescript
22
22
  * const result = await parseFile('/path/to/main.beancount', { recurse: true })
23
- * // All entries from included files are merged into the result
23
+ * // All nodes from included files are merged into the result
24
24
  * ```
25
25
  *
26
26
  * @example
@@ -86,7 +86,7 @@ export const parseFile = async (filepath, options = {}) => {
86
86
  * @param filepath - Path to the Beancount file to parse
87
87
  * @param visited - Set of already visited file paths (absolute)
88
88
  * @param fsHelpers - File system helpers for reading files and handling paths
89
- * @returns A ParseResult containing all parsed entries
89
+ * @returns A ParseResult containing all parsed nodes
90
90
  */
91
91
  const parseFileRecursive = async (filepath, visited, fsHelpers) => {
92
92
  const absolutePath = fsHelpers.resolvePath(filepath);
@@ -97,18 +97,18 @@ const parseFileRecursive = async (filepath, visited, fsHelpers) => {
97
97
  visited.add(absolutePath);
98
98
  const content = await fsHelpers.readFile(absolutePath);
99
99
  const result = parse(content);
100
- const allEntries = [];
100
+ const allNodes = [];
101
101
  const baseDir = fsHelpers.dirname(absolutePath);
102
- for (const entry of result.entries) {
103
- if (entry.type === 'include') {
104
- const includeEntry = entry;
105
- const includePath = fsHelpers.resolveRelative(baseDir, includeEntry.filename);
102
+ for (const node of result.nodes) {
103
+ if (node.type === 'include') {
104
+ const includeNode = node;
105
+ const includePath = fsHelpers.resolveRelative(baseDir, includeNode.filename);
106
106
  const includeResult = await parseFileRecursive(includePath, visited, fsHelpers);
107
- allEntries.push(...includeResult.entries);
107
+ allNodes.push(...includeResult.nodes);
108
108
  }
109
109
  else {
110
- allEntries.push(entry);
110
+ allNodes.push(node);
111
111
  }
112
112
  }
113
- return new ParseResult(allEntries);
113
+ return new ParseResult(allNodes);
114
114
  };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Splits a Beancount file string into an array of source fragments.
3
+ * Each fragment is represented as an array of strings.
4
+ *
5
+ * This function handles:
6
+ * - Splitting on blank lines between fragments
7
+ * - Detecting new fragments based on indentation
8
+ * - Preserving multi-line fragments
9
+ *
10
+ * @param source - The complete Beancount file content as a string
11
+ * @returns An array where each element is an array of strings representing one source fragment
12
+ * @internal
13
+ */
14
+ export declare const splitStringIntoSourceFragments: (source: string) => string[][];
@@ -0,0 +1,48 @@
1
+ import { countChar } from './countChar.mjs';
2
+ import { stringAwareSplitLine } from './stringAwareSplitLine.mjs';
3
+ /**
4
+ * Splits a Beancount file string into an array of source fragments.
5
+ * Each fragment is represented as an array of strings.
6
+ *
7
+ * This function handles:
8
+ * - Splitting on blank lines between fragments
9
+ * - Detecting new fragments based on indentation
10
+ * - Preserving multi-line fragments
11
+ *
12
+ * @param source - The complete Beancount file content as a string
13
+ * @returns An array where each element is an array of strings representing one source fragment
14
+ * @internal
15
+ */
16
+ export const splitStringIntoSourceFragments = (source) => {
17
+ const lines = source.split('\n');
18
+ // split up the file into source fragments
19
+ let inFragment = false;
20
+ let inString = false;
21
+ const fragments = lines.reduce((acc, line) => {
22
+ if (!inString) {
23
+ if (line.trim() === '') {
24
+ // empty newline, next fragment starts
25
+ inFragment = false;
26
+ }
27
+ if (!line.startsWith(' ') && inFragment) {
28
+ // no indent, new fragment
29
+ inFragment = false;
30
+ }
31
+ if (!inFragment) {
32
+ acc.push([]);
33
+ inFragment = true;
34
+ }
35
+ }
36
+ acc[acc.length - 1].push(line);
37
+ // After a blank line, ensure next line starts a new fragment
38
+ if (!inString && line.trim() === '') {
39
+ inFragment = false;
40
+ }
41
+ // odd number of ", we're in an unclosed string
42
+ if (countChar(line, '"') % 2 === 1) {
43
+ inString = !inString;
44
+ }
45
+ return acc;
46
+ }, []);
47
+ return fragments.map((lines) => stringAwareSplitLine(lines.join('\n')));
48
+ };