unist-plugin-log-tree 1.0.1 → 1.0.2

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.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 ipikuka
3
+ Copyright (c) 2026 ipikuka
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -164,7 +164,7 @@ All options are optional.
164
164
  type UnistLogTreeOptions = {
165
165
  test?: Test;
166
166
  preserveSubtree?: boolean;
167
- preservePositions?: boolean;
167
+ excludeKeys?: string[];
168
168
  depth?: number | null;
169
169
  indentation?: number;
170
170
  label?: string;
@@ -208,17 +208,19 @@ Example:
208
208
  }))
209
209
  ```
210
210
 
211
- ### `preservePositions`
211
+ ### `excludeKeys`
212
212
 
213
- Type: `boolean`
214
- Default: `false`
213
+ Type: `string[]`
214
+ Default: `[]` *empty array*
215
215
 
216
- Controls whether `position` data is included in the output.
216
+ An array of property names to be recursively removed from the AST nodes before logging. This is useful for reducing noise by hiding metadata like `position`, `loc`, or `range`. Use this to filter out unwanted node data during logging.
217
217
 
218
218
  ```js
219
- .use(logTree({ preservePositions: true }))
219
+ .use(logTree({ excludeKeys: ["position"] }))
220
220
  ```
221
221
 
222
+ Strips `position` from the AST output. Output of the tree will not contain `position` data.
223
+
222
224
  ### `depth`
223
225
 
224
226
  Type: `number | null`
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface Node extends UnistNode {
7
7
  export type UnistLogTreeOptions = {
8
8
  depth?: number | null;
9
9
  indentation?: number;
10
- preservePositions?: boolean;
10
+ excludeKeys?: string[];
11
11
  test?: Test;
12
12
  preserveSubtree?: boolean;
13
13
  label?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAO1C,MAAM,WAAW,IAAK,SAAQ,SAAS;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAmBF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAqJ9E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,KAAK,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAO1C,MAAM,WAAW,IAAK,SAAQ,SAAS;IACrC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAmBF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,OAAO,CAAC,EAAE,mBAAmB,GAAG,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAqJ9E"}
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { is } from "unist-util-is";
2
2
  const DEFAULT_SETTINGS = {
3
3
  depth: null,
4
4
  indentation: 2,
5
- preservePositions: false,
5
+ excludeKeys: [],
6
6
  test: undefined,
7
7
  preserveSubtree: true,
8
8
  label: undefined,
@@ -128,7 +128,7 @@ export default function plugin(options) {
128
128
  console.log(`[unist-log-tree] ${settings.label}`);
129
129
  }
130
130
  const output = JSON.parse(JSON.stringify(targetTree, (key, value) => {
131
- if (!settings.preservePositions && key === "position") {
131
+ if (settings.excludeKeys.includes(key)) {
132
132
  return undefined;
133
133
  }
134
134
  return value;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,EAAE,EAAE,MAAM,eAAe,CAAC;AAoBnC,MAAM,gBAAgB,GAAwB;IAC5C,KAAK,EAAE,IAAI;IACX,WAAW,EAAE,CAAC;IACd,iBAAiB,EAAE,KAAK;IACxB,IAAI,EAAE,SAAS;IACf,eAAe,EAAE,IAAI;IACrB,KAAK,EAAE,SAAS;IAChB,OAAO,EAAE,IAAI;CACd,CAAC;AASF;;;;;;;;;;GAUG;AACH,MAAM,CAAC,OAAO,UAAU,MAAM,CAAC,OAA6B;IAC1D,OAAO,SAAS,QAAQ;QACtB,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,gBAAgB,EAAE,OAAO,CAA6B,CAAC;QAE1F,aAAa;QACb,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,OAAO,SAAS,WAAW,CAAC,IAAU;gBACpC,OAAO,IAAI,CAAC;YACd,CAAC,CAAC;QACJ,CAAC;QAED;;WAEG;QACH,SAAS,WAAW,CAAC,KAAc;YACjC,OAAO,CACL,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;gBACnB,KAAmB,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,MAAM,IAAI,IAAI,CAAC,CACzF,CAAC;QACJ,CAAC;QAED;;WAEG;QACH,SAAS,MAAM,CAAC,KAAc;YAC5B,OAAO,CAAC,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,CAAC;QACjE,CAAC;QAED;;;WAGG;QACH,+DAA+D;QAC/D,0CAA0C;QAE1C,0CAA0C;QAC1C,sCAAsC;QAEtC,4BAA4B;QAC5B,6DAA6D;QAC7D,8CAA8C;QAC9C,UAAU;QAEV,uEAAuE;QACvE,QAAQ;QAER,gCAAgC;QAEhC,4BAA4B;QAC5B,yCAAyC;QACzC,QAAQ;QAER,oBAAoB;QACpB,MAAM;QAEN,oCAAoC;QAEpC,yCAAyC;QACzC,4BAA4B;QAC5B,MAAM;QAEN,mBAAmB;QACnB,IAAI;QAEJ;;;WAGG;QACH,SAAS,iBAAiB,CAAC,IAAU,EAAE,IAAU;YAC/C,MAAM,MAAM,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;YAErC,SAAS,KAAK,CAAC,IAAU;gBACvB,MAAM,OAAO,GAAG,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAE/B,IAAI,aAAa,GAAG,KAAK,CAAC;gBAE1B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;oBACvB,IAAI,GAAG,KAAK,UAAU;wBAAE,SAAS;oBAEjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;oBAExB,IAAI,CAAC,KAAK;wBAAE,SAAS;oBAErB,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;wBACvB,IAAI,YAAY,GAAG,KAAK,CAAC;wBAEzB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;4BAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;4BAEtB,iCAAiC;4BACjC,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gCACjB,YAAY,GAAG,IAAI,CAAC;gCAEpB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;gCAEzB,IAAI,CAAC,IAAI,EAAE,CAAC;oCACV,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;gCACrB,CAAC;qCAAM,CAAC;oCACN,aAAa,GAAG,IAAI,CAAC;gCACvB,CAAC;4BACH,CAAC;wBACH,CAAC;wBAED,IAAI,YAAY,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;4BACvC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC;wBACjB,CAAC;oBACH,CAAC;yBAAM,IAAI,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;wBACzB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;wBAE1B,IAAI,CAAC,IAAI,EAAE,CAAC;4BACV,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC;wBACnB,CAAC;6BAAM,CAAC;4BACN,aAAa,GAAG,IAAI,CAAC;wBACvB,CAAC;oBACH,CAAC;gBACH,CAAC;gBAED,IAAI,OAAO,IAAI,QAAQ,CAAC,eAAe,KAAK,KAAK,EAAE,CAAC;oBAClD,OAAO,IAAI,CAAC,CAAC,sBAAsB;gBACrC,CAAC;gBAED,IAAI,OAAO;oBAAE,OAAO,IAAI,CAAC;gBAEzB,OAAO,aAAa,CAAC;YACvB,CAAC;YAED,KAAK,CAAC,MAAM,CAAC,CAAC;YACd,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,OAAO,SAAS,WAAW,CAAC,IAAU;YACpC,MAAM,UAAU,GAAG,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC,CAAC,iBAAiB,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAEzF,IAAI,QAAQ,CAAC,KAAK,EAAE,CAAC;gBACnB,OAAO,CAAC,GAAG,CAAC,oBAAoB,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;YACpD,CAAC;YAED,MAAM,MAAM,GAAS,IAAI,CAAC,KAAK,CAC7B,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC,GAAW,EAAE,KAAc,EAAE,EAAE;gBACzD,IAAI,CAAC,QAAQ,CAAC,iBAAiB,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;oBACtD,OAAO,SAAS,CAAC;gBACnB,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC,CAAC,CACH,CAAC;YAEF,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC;QACjD,CAAC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import type { Plugin } from \"unified\";\nimport type { Node as UnistNode } from \"unist\";\nimport type { Test } from \"unist-util-is\";\nimport { is } from \"unist-util-is\";\n\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\ntype PartiallyRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\nexport interface Node extends UnistNode {\n [key: string]: unknown;\n}\n\nexport type UnistLogTreeOptions = {\n depth?: number | null;\n indentation?: number;\n preservePositions?: boolean;\n test?: Test;\n preserveSubtree?: boolean;\n label?: string;\n enabled?: boolean;\n};\n\nconst DEFAULT_SETTINGS: UnistLogTreeOptions = {\n depth: null,\n indentation: 2,\n preservePositions: false,\n test: undefined,\n preserveSubtree: true,\n label: undefined,\n enabled: true,\n};\n\ntype PartiallyRequiredOptions = Prettify<\n PartiallyRequired<\n UnistLogTreeOptions,\n \"depth\" | \"indentation\" | \"preservePositions\" | \"enabled\"\n >\n>;\n\n/**\n * Debug utility plugin for unified processors.\n *\n * Logs the current unist tree (mdast, hast, estree, etc.)\n * with optional filtering and formatting controls.\n *\n * If `test` is provided, only matching nodes and their\n * parent chain are preserved in the output.\n *\n * The original tree is never mutated.\n */\nexport default function plugin(options?: UnistLogTreeOptions): Plugin<[], Node> {\n return function attacher() {\n const settings = Object.assign({}, DEFAULT_SETTINGS, options) as PartiallyRequiredOptions;\n\n // early exit\n if (!settings.enabled) {\n return function transformer(tree: Node) {\n return tree;\n };\n }\n\n /*\n * Type guard to check if an array of Node\n */\n function isNodeArray(value: unknown): value is Node[] {\n return (\n Array.isArray(value) &&\n (value as unknown[]).every((item) => item && typeof item === \"object\" && \"type\" in item)\n );\n }\n\n /*\n * Type guard to check if a node is a unist Node\n */\n function isNode(value: unknown): value is Node {\n return !!value && typeof value === \"object\" && \"type\" in value;\n }\n\n /**\n * Builds a minimal subtree that keeps only nodes matching the test and their parent chain.\n * It uses prune algoritm that works on children key, so with mdast and hast okey but with esast doesn't work\n */\n // function buildFilteredTreeEx(root: Node, test: Test): Node {\n // const cloned = structuredClone(root);\n\n // function prune(node: Node): boolean {\n // const matched = is(node, test);\n\n // if (isParent(node)) {\n // if (matched && settings.preserveSubtree !== false) {\n // return true; // don't touch subtree\n // }\n\n // node.children = node.children.filter((child) => prune(child));\n // }\n\n // if (matched) return true;\n\n // if (isParent(node)) {\n // return node.children.length > 0;\n // }\n\n // return false;\n // }\n\n // const keepRoot = prune(cloned);\n\n // if (!keepRoot && isParent(cloned)) {\n // cloned.children = [];\n // }\n\n // return cloned;\n // }\n\n /**\n * Builds a minimal subtree that keeps only nodes matching the test and their parent chain.\n * It is a universal travel algoritim works with any AST like mdast, hast, esast and others\n */\n function buildFilteredTree(root: Node, test: Test): Node {\n const cloned = structuredClone(root);\n\n function prune(node: Node): boolean {\n const matched = is(node, test);\n\n let hasChildMatch = false;\n\n for (const key in node) {\n if (key === \"position\") continue;\n\n const value = node[key];\n\n if (!value) continue;\n\n if (isNodeArray(value)) {\n let containsNode = false;\n\n for (let i = value.length - 1; i >= 0; i--) {\n const item = value[i];\n\n /* v8 ignore next -- @preserve */\n if (isNode(item)) {\n containsNode = true;\n\n const keep = prune(item);\n\n if (!keep) {\n value.splice(i, 1);\n } else {\n hasChildMatch = true;\n }\n }\n }\n\n if (containsNode && value.length === 0) {\n node[key] = [];\n }\n } else if (isNode(value)) {\n const keep = prune(value);\n\n if (!keep) {\n delete node[key];\n } else {\n hasChildMatch = true;\n }\n }\n }\n\n if (matched && settings.preserveSubtree !== false) {\n return true; // don't touch subtree\n }\n\n if (matched) return true;\n\n return hasChildMatch;\n }\n\n prune(cloned);\n return cloned;\n }\n\n return function transformer(tree: Node) {\n const targetTree = settings.test != null ? buildFilteredTree(tree, settings.test) : tree;\n\n if (settings.label) {\n console.log(`[unist-log-tree] ${settings.label}`);\n }\n\n const output: Node = JSON.parse(\n JSON.stringify(targetTree, (key: string, value: unknown) => {\n if (!settings.preservePositions && key === \"position\") {\n return undefined;\n }\n return value;\n }),\n );\n\n console.dir(output, { depth: settings.depth });\n };\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Plugin } from \"unified\";\nimport type { Node as UnistNode } from \"unist\";\nimport type { Test } from \"unist-util-is\";\nimport { is } from \"unist-util-is\";\n\ntype Prettify<T> = { [K in keyof T]: T[K] } & {};\n\ntype PartiallyRequired<T, K extends keyof T> = Omit<T, K> & Required<Pick<T, K>>;\n\nexport interface Node extends UnistNode {\n [key: string]: unknown;\n}\n\nexport type UnistLogTreeOptions = {\n depth?: number | null;\n indentation?: number;\n excludeKeys?: string[];\n test?: Test;\n preserveSubtree?: boolean;\n label?: string;\n enabled?: boolean;\n};\n\nconst DEFAULT_SETTINGS: UnistLogTreeOptions = {\n depth: null,\n indentation: 2,\n excludeKeys: [],\n test: undefined,\n preserveSubtree: true,\n label: undefined,\n enabled: true,\n};\n\ntype PartiallyRequiredOptions = Prettify<\n PartiallyRequired<\n UnistLogTreeOptions,\n \"depth\" | \"indentation\" | \"preserveSubtree\" | \"enabled\" | \"excludeKeys\"\n >\n>;\n\n/**\n * Debug utility plugin for unified processors.\n *\n * Logs the current unist tree (mdast, hast, estree, etc.)\n * with optional filtering and formatting controls.\n *\n * If `test` is provided, only matching nodes and their\n * parent chain are preserved in the output.\n *\n * The original tree is never mutated.\n */\nexport default function plugin(options?: UnistLogTreeOptions): Plugin<[], Node> {\n return function attacher() {\n const settings = Object.assign({}, DEFAULT_SETTINGS, options) as PartiallyRequiredOptions;\n\n // early exit\n if (!settings.enabled) {\n return function transformer(tree: Node) {\n return tree;\n };\n }\n\n /*\n * Type guard to check if an array of Node\n */\n function isNodeArray(value: unknown): value is Node[] {\n return (\n Array.isArray(value) &&\n (value as unknown[]).every((item) => item && typeof item === \"object\" && \"type\" in item)\n );\n }\n\n /*\n * Type guard to check if a node is a unist Node\n */\n function isNode(value: unknown): value is Node {\n return !!value && typeof value === \"object\" && \"type\" in value;\n }\n\n /**\n * Builds a minimal subtree that keeps only nodes matching the test and their parent chain.\n * It uses prune algoritm that works on children key, so with mdast and hast okey but with esast doesn't work\n */\n // function buildFilteredTreeEx(root: Node, test: Test): Node {\n // const cloned = structuredClone(root);\n\n // function prune(node: Node): boolean {\n // const matched = is(node, test);\n\n // if (isParent(node)) {\n // if (matched && settings.preserveSubtree !== false) {\n // return true; // don't touch subtree\n // }\n\n // node.children = node.children.filter((child) => prune(child));\n // }\n\n // if (matched) return true;\n\n // if (isParent(node)) {\n // return node.children.length > 0;\n // }\n\n // return false;\n // }\n\n // const keepRoot = prune(cloned);\n\n // if (!keepRoot && isParent(cloned)) {\n // cloned.children = [];\n // }\n\n // return cloned;\n // }\n\n /**\n * Builds a minimal subtree that keeps only nodes matching the test and their parent chain.\n * It is a universal travel algoritim works with any AST like mdast, hast, esast and others\n */\n function buildFilteredTree(root: Node, test: Test): Node {\n const cloned = structuredClone(root);\n\n function prune(node: Node): boolean {\n const matched = is(node, test);\n\n let hasChildMatch = false;\n\n for (const key in node) {\n if (key === \"position\") continue;\n\n const value = node[key];\n\n if (!value) continue;\n\n if (isNodeArray(value)) {\n let containsNode = false;\n\n for (let i = value.length - 1; i >= 0; i--) {\n const item = value[i];\n\n /* v8 ignore next -- @preserve */\n if (isNode(item)) {\n containsNode = true;\n\n const keep = prune(item);\n\n if (!keep) {\n value.splice(i, 1);\n } else {\n hasChildMatch = true;\n }\n }\n }\n\n if (containsNode && value.length === 0) {\n node[key] = [];\n }\n } else if (isNode(value)) {\n const keep = prune(value);\n\n if (!keep) {\n delete node[key];\n } else {\n hasChildMatch = true;\n }\n }\n }\n\n if (matched && settings.preserveSubtree !== false) {\n return true; // don't touch subtree\n }\n\n if (matched) return true;\n\n return hasChildMatch;\n }\n\n prune(cloned);\n return cloned;\n }\n\n return function transformer(tree: Node) {\n const targetTree = settings.test != null ? buildFilteredTree(tree, settings.test) : tree;\n\n if (settings.label) {\n console.log(`[unist-log-tree] ${settings.label}`);\n }\n\n const output: Node = JSON.parse(\n JSON.stringify(targetTree, (key: string, value: unknown) => {\n if (settings.excludeKeys.includes(key)) {\n return undefined;\n }\n return value;\n }),\n );\n\n console.dir(output, { depth: settings.depth });\n };\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unist-plugin-log-tree",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "A debugging plugin for the unified ecosystem that logs unist syntax trees without transforming.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",