Working on API surface.
This commit is contained in:
parent
a0791b0c69
commit
28de2a8e20
3
packages/js/core/dist/document.cjs
vendored
3
packages/js/core/dist/document.cjs
vendored
@ -1 +1,2 @@
|
|||||||
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const l=require("./parser.cjs");function s(a,i=" "){let e=null,t=l.LineData(),n=!1;async function r(){return e=await a(),e===null?n=!0:l.parseLine(e,t,i),{line:e,lineData:t,ended:n,next:r,current:u}}function u(){return{line:e,lineData:t,ended:n,next:r,current:u}}return{next:r,current:u}}exports.document=s;
|
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const s=require("./parser.cjs");function a(i,u=" "){let r=s.createLineData(null,u);const t={ended:!1,clone(){return a(i.clone(),u)},async next(){if(r.line=await i.next(),r.line===null)return!0;s.parseLine(r)},current(){return t},line(){var e;return(e=r.line)==null?void 0:e.slice(r.offsetHead)},head(){var e;return(e=r.line)==null?void 0:e.slice(r.offsetHead,r.offsetTail)},tail(){var e;return(e=r.line)==null?void 0:e.slice(r.offsetTail)},async content(e=-1,n=[]){var l;return e===-1&&(e=r.level+1),await t.next()||r.level<e?n.join(`
|
||||||
|
`):(n.push(((l=r.line)==null?void 0:l.slice(e))||""),t.content(e,n))},async seek(e,n=-1){return n===-1&&(n=r.level),await t.next()?!1:t.head()===e?t:r.level<n?!1:t.seek(e,n)}};return t}exports.useDocument=a;
|
||||||
|
50
packages/js/core/dist/document.js
vendored
50
packages/js/core/dist/document.js
vendored
@ -1,14 +1,42 @@
|
|||||||
import { parseLine as a, LineData as o } from "./parser.js";
|
import { parseLine as f, createLineData as s } from "./parser.js";
|
||||||
function f(u, i = " ") {
|
function d(t, l = " ") {
|
||||||
let e = null, n = o(), t = !1;
|
let n = s(null, l);
|
||||||
async function r() {
|
const i = {
|
||||||
return e = await u(), e === null ? t = !0 : a(e, n, i), { line: e, lineData: n, ended: t, next: r, current: l };
|
ended: !1,
|
||||||
}
|
clone() {
|
||||||
function l() {
|
return d(t.clone(), l);
|
||||||
return { line: e, lineData: n, ended: t, next: r, current: l };
|
},
|
||||||
}
|
async next() {
|
||||||
return { next: r, current: l };
|
if (n.line = await t.next(), n.line === null)
|
||||||
|
return !0;
|
||||||
|
f(n);
|
||||||
|
},
|
||||||
|
current() {
|
||||||
|
return i;
|
||||||
|
},
|
||||||
|
line() {
|
||||||
|
var e;
|
||||||
|
return (e = n.line) == null ? void 0 : e.slice(n.offsetHead);
|
||||||
|
},
|
||||||
|
head() {
|
||||||
|
var e;
|
||||||
|
return (e = n.line) == null ? void 0 : e.slice(n.offsetHead, n.offsetTail);
|
||||||
|
},
|
||||||
|
tail() {
|
||||||
|
var e;
|
||||||
|
return (e = n.line) == null ? void 0 : e.slice(n.offsetTail);
|
||||||
|
},
|
||||||
|
async content(e = -1, r = []) {
|
||||||
|
var u;
|
||||||
|
return e === -1 && (e = n.level + 1), await i.next() || n.level < e ? r.join(`
|
||||||
|
`) : (r.push(((u = n.line) == null ? void 0 : u.slice(e)) || ""), i.content(e, r));
|
||||||
|
},
|
||||||
|
async seek(e, r = -1) {
|
||||||
|
return r === -1 && (r = n.level), await i.next() ? !1 : i.head() === e ? i : n.level < r ? !1 : i.seek(e, r);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return i;
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
f as document
|
d as useDocument
|
||||||
};
|
};
|
||||||
|
2
packages/js/core/dist/index.cjs
vendored
2
packages/js/core/dist/index.cjs
vendored
@ -1 +1 @@
|
|||||||
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./parser.cjs"),r=require("./document.cjs");exports.LineData=e.LineData;exports.parseLine=e.parseLine;exports.document=r.document;
|
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e=require("./parser.cjs"),r=require("./document.cjs");exports.createLineData=e.createLineData;exports.parseLine=e.parseLine;exports.useDocument=r.useDocument;
|
||||||
|
10
packages/js/core/dist/index.js
vendored
10
packages/js/core/dist/index.js
vendored
@ -1,7 +1,7 @@
|
|||||||
import { LineData as r, parseLine as t } from "./parser.js";
|
import { createLineData as o, parseLine as t } from "./parser.js";
|
||||||
import { document as m } from "./document.js";
|
import { useDocument as m } from "./document.js";
|
||||||
export {
|
export {
|
||||||
r as LineData,
|
o as createLineData,
|
||||||
m as document,
|
t as parseLine,
|
||||||
t as parseLine
|
m as useDocument
|
||||||
};
|
};
|
||||||
|
2
packages/js/core/dist/parser.cjs
vendored
2
packages/js/core/dist/parser.cjs
vendored
@ -1 +1 @@
|
|||||||
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function s(){return{type:0,level:0,offsetHead:0,offsetTail:0}}function p(r,e,f=" "){if(typeof r!="string")throw new Error("'line' must be a string");if(typeof e!="object"||!e||typeof e.type!="number"||typeof e.level!="number")throw new Error("'lineData' must be an object with 'type' and 'level' integer properties");if(typeof f!="string"||f.length===0||f.length>1)throw new Error("'indent' must be a single-character string");let o=0,t=0;if(!r.length)e.type===1&&(t+=1),e.type===0&&(t=e.level),e.type=o,e.level=t,e.offsetHead=0,e.offsetTail=0;else{for(o=1;r[t]===f&&t<=e.level+1;)++t;for(e.type=o,e.level=t,e.offsetHead=t,e.offsetTail=t;r[e.offsetTail]&&r[e.offsetTail]!==" ";)++e.offsetTail}return e}exports.LineData=s;exports.parseLine=p;
|
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function f(e=null,r=" "){return{line:e,indent:r,type:0,level:0,offsetHead:0,offsetTail:0}}function o(e){if(typeof e!="object"||!e||typeof e.type!="number"||typeof e.level!="number")throw new Error("'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties");if(typeof e.indent!="string"||e.indent.length===0||e.indent.length>1)throw new Error("'lineData.indent' must be a single-character string");if(typeof e.line!="string")throw new Error("'lineData.line' must be a string");let r=0,t=0;if(!e.line.length)e.type===1&&(t+=1),e.type===0&&(t=e.level),e.type=r,e.level=t,e.offsetHead=0,e.offsetTail=0;else{for(r=1;e.line[t]===e.indent&&t<=e.level+1;)++t;for(e.type=r,e.level=t,e.offsetHead=t,e.offsetTail=t;e.line[e.offsetTail]&&e.line[e.offsetTail]!==" ";)++e.offsetTail}return e}exports.createLineData=f;exports.parseLine=o;
|
||||||
|
30
packages/js/core/dist/parser.js
vendored
30
packages/js/core/dist/parser.js
vendored
@ -1,25 +1,25 @@
|
|||||||
function s() {
|
function t(e = null, r = " ") {
|
||||||
return { type: 0, level: 0, offsetHead: 0, offsetTail: 0 };
|
return { line: e, indent: r, type: 0, level: 0, offsetHead: 0, offsetTail: 0 };
|
||||||
}
|
}
|
||||||
function p(r, e, t = " ") {
|
function o(e) {
|
||||||
if (typeof r != "string")
|
|
||||||
throw new Error("'line' must be a string");
|
|
||||||
if (typeof e != "object" || !e || typeof e.type != "number" || typeof e.level != "number")
|
if (typeof e != "object" || !e || typeof e.type != "number" || typeof e.level != "number")
|
||||||
throw new Error("'lineData' must be an object with 'type' and 'level' integer properties");
|
throw new Error("'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties");
|
||||||
if (typeof t != "string" || t.length === 0 || t.length > 1)
|
if (typeof e.indent != "string" || e.indent.length === 0 || e.indent.length > 1)
|
||||||
throw new Error("'indent' must be a single-character string");
|
throw new Error("'lineData.indent' must be a single-character string");
|
||||||
let o = 0, f = 0;
|
if (typeof e.line != "string")
|
||||||
if (!r.length)
|
throw new Error("'lineData.line' must be a string");
|
||||||
e.type === 1 && (f += 1), e.type === 0 && (f = e.level), e.type = o, e.level = f, e.offsetHead = 0, e.offsetTail = 0;
|
let r = 0, f = 0;
|
||||||
|
if (!e.line.length)
|
||||||
|
e.type === 1 && (f += 1), e.type === 0 && (f = e.level), e.type = r, e.level = f, e.offsetHead = 0, e.offsetTail = 0;
|
||||||
else {
|
else {
|
||||||
for (o = 1; r[f] === t && f <= e.level + 1; )
|
for (r = 1; e.line[f] === e.indent && f <= e.level + 1; )
|
||||||
++f;
|
++f;
|
||||||
for (e.type = o, e.level = f, e.offsetHead = f, e.offsetTail = f; r[e.offsetTail] && r[e.offsetTail] !== " "; )
|
for (e.type = r, e.level = f, e.offsetHead = f, e.offsetTail = f; e.line[e.offsetTail] && e.line[e.offsetTail] !== " "; )
|
||||||
++e.offsetTail;
|
++e.offsetTail;
|
||||||
}
|
}
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
export {
|
export {
|
||||||
s as LineData,
|
t as createLineData,
|
||||||
p as parseLine
|
o as parseLine
|
||||||
};
|
};
|
||||||
|
2
packages/js/core/dist/readers/js-string.cjs
vendored
Normal file
2
packages/js/core/dist/readers/js-string.cjs
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function i(r,n=0){const t=Array.isArray(r)?r:r.split(`
|
||||||
|
`),e={index:n-1,next:()=>(e.index++,e.index>=t.length?null:t[e.index]),clone:l=>i(r,l==null?n:e.index)};return e}exports.createStringReader=i;
|
12
packages/js/core/dist/readers/js-string.js
vendored
Normal file
12
packages/js/core/dist/readers/js-string.js
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
function l(e, r = 0) {
|
||||||
|
const t = Array.isArray(e) ? e : e.split(`
|
||||||
|
`), n = {
|
||||||
|
index: r - 1,
|
||||||
|
next: () => (n.index++, n.index >= t.length ? null : t[n.index]),
|
||||||
|
clone: (i) => l(e, i == null ? r : n.index)
|
||||||
|
};
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
l as createStringReader
|
||||||
|
};
|
1
packages/js/core/dist/readers/node-readline.cjs
vendored
Normal file
1
packages/js/core/dist/readers/node-readline.cjs
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const e={};function r(t){const a=e.createInterface({input:e.createReadStream(t,"utf-8")})[Symbol.asyncIterator]();return async()=>(await a.next()).value}exports.createReadlineReader=r;
|
10
packages/js/core/dist/readers/node-readline.js
vendored
Normal file
10
packages/js/core/dist/readers/node-readline.js
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
const e = {};
|
||||||
|
function n(t) {
|
||||||
|
const a = e.createInterface({
|
||||||
|
input: e.createReadStream(t, "utf-8")
|
||||||
|
})[Symbol.asyncIterator]();
|
||||||
|
return async () => (await a.next()).value;
|
||||||
|
}
|
||||||
|
export {
|
||||||
|
n as createReadlineReader
|
||||||
|
};
|
@ -4,8 +4,26 @@
|
|||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
"import": "./dist/index.js",
|
".": {
|
||||||
"require": "./dist/index.cjs"
|
"import": "./dist/index.js",
|
||||||
|
"require": "./dist/index.cjs"
|
||||||
|
},
|
||||||
|
"./parser": {
|
||||||
|
"import": "./dist/parser.js",
|
||||||
|
"require": "./dist/parser.cjs"
|
||||||
|
},
|
||||||
|
"./document": {
|
||||||
|
"import": "./dist/document.js",
|
||||||
|
"require": "./dist/document.cjs"
|
||||||
|
},
|
||||||
|
"./readers/node-readline": {
|
||||||
|
"import": "./dist/readers/node-readline.js",
|
||||||
|
"require": "./dist/readers/node-readline.cjs"
|
||||||
|
},
|
||||||
|
"./readers/js-string": {
|
||||||
|
"import": "./dist/readers/js-string.js",
|
||||||
|
"require": "./dist/readers/js-string.cjs"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest ./src",
|
"test": "vitest ./src",
|
||||||
|
@ -1,20 +1,74 @@
|
|||||||
import { LineData, parseLine } from './parser'
|
import type { Reader } from './readers/reader'
|
||||||
|
import { createLineData, parseLine } from './parser'
|
||||||
|
|
||||||
export function document (nextFn: () => Promise<string|null>, indent: string = ' ') {
|
type Document = {
|
||||||
let line: string|null = null
|
ended: boolean,
|
||||||
let lineData = LineData()
|
clone: () => Document,
|
||||||
let ended = false
|
next: () => Promise<Document>
|
||||||
|
current: () => Document
|
||||||
async function next() {
|
line: () => string,
|
||||||
line = await nextFn()
|
head: () => string,
|
||||||
if (line === null) ended = true
|
tail: () => string,
|
||||||
else parseLine(line, lineData, indent)
|
content: (contentLevel: number, lines: string[]) => Promise<string>,
|
||||||
return { line, lineData, ended, next, current }
|
seek: (matchHead: string, contentLevel: number) => Promise<Document|false>
|
||||||
}
|
}
|
||||||
|
|
||||||
function current() {
|
export function useDocument (reader: Reader, indent: string = ' '): Document {
|
||||||
return { line, lineData, ended, next, current }
|
let lineData = createLineData(null, indent)
|
||||||
}
|
|
||||||
|
const document = {
|
||||||
return { next, current }
|
ended: false,
|
||||||
|
|
||||||
|
clone() {
|
||||||
|
return useDocument(reader.clone(), indent)
|
||||||
|
},
|
||||||
|
|
||||||
|
async next() {
|
||||||
|
lineData.line = await reader.next()
|
||||||
|
if (lineData.line === null) return true
|
||||||
|
else parseLine(lineData)
|
||||||
|
},
|
||||||
|
|
||||||
|
current() {
|
||||||
|
return document
|
||||||
|
},
|
||||||
|
|
||||||
|
line() {
|
||||||
|
return lineData.line?.slice(lineData.offsetHead)
|
||||||
|
},
|
||||||
|
|
||||||
|
head () {
|
||||||
|
return lineData.line?.slice(lineData.offsetHead, lineData.offsetTail)
|
||||||
|
},
|
||||||
|
|
||||||
|
tail () {
|
||||||
|
return lineData.line?.slice(lineData.offsetTail)
|
||||||
|
},
|
||||||
|
|
||||||
|
async content (contentLevel = -1, lines: string[] = []): Promise<string> {
|
||||||
|
if (contentLevel === -1) contentLevel = lineData.level + 1
|
||||||
|
|
||||||
|
const ended = await document.next()
|
||||||
|
if (ended) return lines.join('\n')
|
||||||
|
|
||||||
|
if (lineData.level < contentLevel) return lines.join('\n')
|
||||||
|
|
||||||
|
lines.push(lineData.line?.slice(contentLevel) || '')
|
||||||
|
return document.content(contentLevel, lines)
|
||||||
|
},
|
||||||
|
|
||||||
|
async seek (matchHead: string, contentLevel = -1): Promise<Document|false> {
|
||||||
|
if (contentLevel === -1) contentLevel = lineData.level
|
||||||
|
|
||||||
|
const ended = await document.next()
|
||||||
|
if (ended) return false
|
||||||
|
|
||||||
|
if (document.head() === matchHead) return document
|
||||||
|
if (lineData.level < contentLevel) return false
|
||||||
|
|
||||||
|
return document.seek(matchHead, contentLevel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return document
|
||||||
}
|
}
|
||||||
|
@ -1,145 +1,143 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { LineData, parseLine } from './parser'
|
import { createLineData, parseLine } from './parser'
|
||||||
|
|
||||||
describe(`LineData`, () => {
|
describe(`LineData`, () => {
|
||||||
it(`is an object`, () => {
|
it(`is an object`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(lineData).toBeTypeOf(`object`)
|
expect(lineData).toBeTypeOf(`object`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`has four properties`, () => {
|
it(`has five properties`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(Object.keys(lineData).length).to.equal(4)
|
expect(Object.keys(lineData).length).to.equal(6)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`'line' is a string|null initialized to null`, () => {
|
||||||
|
const lineData = createLineData()
|
||||||
|
expect(lineData.level).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`'type' is an integer initialized to zero`, () => {
|
it(`'type' is an integer initialized to zero`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(lineData.level).to.equal(0)
|
expect(lineData.level).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`'level' is an integer initialized to zero`, () => {
|
it(`'level' is an integer initialized to zero`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(lineData.type).to.equal(0)
|
expect(lineData.type).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`'offsetHead' is an integer initialized to zero`, () => {
|
it(`'offsetHead' is an integer initialized to zero`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(lineData.offsetHead).to.equal(0)
|
expect(lineData.offsetHead).to.equal(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`'offsetTail' is an integer initialized to zero`, () => {
|
it(`'offsetTail' is an integer initialized to zero`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(lineData.offsetTail).to.equal(0)
|
expect(lineData.offsetTail).to.equal(0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe(`parseLine`, () => {
|
describe(`parseLine`, () => {
|
||||||
it(`Requres 'line' to be a string`, () => {
|
it(`Requres 'lineData' to be an object with a string line property and numeric level and type properties`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
expect(() => parseLine(0, lineData)).toThrowError(`'line' must be a string`)
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine([], lineData)).toThrowError(`'line' must be a string`)
|
expect(() => parseLine(``, 0)).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine({}, lineData)).toThrowError(`'line' must be a string`)
|
expect(() => parseLine(``, [])).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(null, lineData)).toThrowError(`'line' must be a string`)
|
expect(() => parseLine(``, {})).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(true, lineData)).toThrowError(`'line' must be a string`)
|
expect(() => parseLine(``, null)).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(() => {}, lineData)).toThrowError(`'line' must be a string`)
|
expect(() => parseLine(``, true)).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
})
|
|
||||||
|
|
||||||
it(`Requres 'lineData' to be an object with numeric level and type properties`, () => {
|
|
||||||
const lineData = LineData()
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, 0)).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
expect(() => parseLine(``, () => {})).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, [])).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
expect(() => parseLine(``, { line: '', level: '', type: 0 })).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, {})).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
expect(() => parseLine(``, { line: '', level: 0, type: null })).toThrowError(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
// @ts-ignore
|
|
||||||
expect(() => parseLine(``, null)).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
|
||||||
// @ts-ignore
|
|
||||||
expect(() => parseLine(``, true)).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
|
||||||
// @ts-ignore
|
|
||||||
expect(() => parseLine(``, () => {})).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
|
||||||
// @ts-ignore
|
|
||||||
expect(() => parseLine(``, { level: '', type: 0 })).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
|
||||||
// @ts-ignore
|
|
||||||
expect(() => parseLine(``, { level: 0, type: null })).toThrowError(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Requres 'indent' to be a single-character string`, () => {
|
it(`Requres 'indent' to be a single-character string`, () => {
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
|
lineData.line = ``
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, lineData, 0)).toThrowError(`'indent' must be a single-character string`)
|
lineData.indent = 0
|
||||||
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, lineData, [])).toThrowError(`'indent' must be a single-character string`)
|
lineData.indent = []
|
||||||
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, lineData, {})).toThrowError(`'indent' must be a single-character string`)
|
lineData.indent = {}
|
||||||
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, lineData, null)).toThrowError(`'indent' must be a single-character string`)
|
lineData.indent = null
|
||||||
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, lineData, true)).toThrowError(`'indent' must be a single-character string`)
|
lineData.indent = true
|
||||||
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
expect(() => parseLine(``, lineData, () => {})).toThrowError(`'indent' must be a single-character string`)
|
lineData.indent = () => {}
|
||||||
expect(() => parseLine(``, lineData, ` `)).toThrowError(`'indent' must be a single-character string`)
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
|
lineData.indent = ` `
|
||||||
|
expect(() => parseLine(lineData)).toThrowError(`'lineData.indent' must be a single-character string`)
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a blank line at indent level 0`, () => {
|
it(`Handles a blank line at indent level 0`, () => {
|
||||||
const line = ``
|
const line = ``
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 0, level: 0, offsetHead: 0, offsetTail: 0 })
|
expect(lineData).to.deep.equal({ line, indent: ` `, type: 0, level: 0, offsetHead: 0, offsetTail: 0 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a line with a single space at indent level 1`, () => {
|
it(`Handles a line with a single space at indent level 1`, () => {
|
||||||
const line = ` `
|
const line = ` `
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 1, offsetHead: 1, offsetTail: 1 })
|
expect(lineData).to.deep.equal({ line, indent: ` `, type: 1, level: 1, offsetHead: 1, offsetTail: 1 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a line with two spaces`, () => {
|
it(`Handles a line with two spaces`, () => {
|
||||||
const line = ` `
|
const line = ` `
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 2, offsetHead: 2, offsetTail: 2 })
|
expect(lineData).to.deep.equal({ line, indent: ` `, type: 1, level: 2, offsetHead: 2, offsetTail: 2 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a normal line at indent level 0`, () => {
|
it(`Handles a normal line at indent level 0`, () => {
|
||||||
const line = `line 1`
|
const line = `line 1`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 0, offsetHead: 0, offsetTail: 4 })
|
expect(lineData).to.deep.equal({ line, indent: ` `, type: 1, level: 0, offsetHead: 0, offsetTail: 4 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a normal line at indent level 1`, () => {
|
it(`Handles a normal line at indent level 1`, () => {
|
||||||
const line = ` line 1`
|
const line = ` line 1`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 1, offsetHead: 1, offsetTail: 5 })
|
expect(lineData).to.deep.equal({ line, indent: ` `, type: 1, level: 1, offsetHead: 1, offsetTail: 5 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a normal line at indent level 2`, () => {
|
it(`Handles a normal line at indent level 2`, () => {
|
||||||
const line = ` line 1`
|
const line = ` line 1`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 2, offsetHead: 2, offsetTail: 6 })
|
expect(lineData).to.deep.equal({ line, indent: ` `, type: 1, level: 2, offsetHead: 2, offsetTail: 6 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a normal line at indent level 1 indented with tabs`, () => {
|
it(`Handles a normal line at indent level 1 indented with tabs`, () => {
|
||||||
const line = `\tline 1`
|
const line = `\tline 1`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line, `\t`)
|
||||||
parseLine(line, lineData, `\t`)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 1, offsetHead: 1, offsetTail: 5 })
|
expect(lineData).to.deep.equal({ line, indent: `\t`, type: 1, level: 1, offsetHead: 1, offsetTail: 5 })
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handles a normal line at indent level 2 indented with tabs`, () => {
|
it(`Handles a normal line at indent level 2 indented with tabs`, () => {
|
||||||
const line = `\t\tline 1`
|
const line = `\t\tline 1`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line, `\t`)
|
||||||
parseLine(line, lineData, `\t`)
|
parseLine(lineData)
|
||||||
expect(lineData).to.deep.equal({ type: 1, level: 2, offsetHead: 2, offsetTail: 6})
|
expect(lineData).to.deep.equal({ line, indent: `\t`, type: 1, level: 2, offsetHead: 2, offsetTail: 6})
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Nests a normal line under a preceding normal line`, () => {
|
it(`Nests a normal line under a preceding normal line`, () => {
|
||||||
@ -148,15 +146,16 @@ describe(`parseLine`, () => {
|
|||||||
' line 2'
|
' line 2'
|
||||||
]
|
]
|
||||||
|
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
const results = lines.map(line => {
|
const results = lines.map(line => {
|
||||||
parseLine(line, lineData)
|
lineData.line = line
|
||||||
|
parseLine(lineData)
|
||||||
return {...lineData}
|
return {...lineData}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).to.deep.equal([
|
expect(results).to.deep.equal([
|
||||||
{ type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
{ line: lines[0], indent: ' ', type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
||||||
{ type: 1, level: 1, offsetHead: 1, offsetTail: 5 }
|
{ line: lines[1], indent: ' ', type: 1, level: 1, offsetHead: 1, offsetTail: 5 }
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -168,17 +167,18 @@ describe(`parseLine`, () => {
|
|||||||
' line 4',
|
' line 4',
|
||||||
]
|
]
|
||||||
|
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
const results = lines.map(line => {
|
const results = lines.map(line => {
|
||||||
parseLine(line, lineData)
|
lineData.line = line
|
||||||
|
parseLine(lineData)
|
||||||
return {...lineData}
|
return {...lineData}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).to.deep.equal([
|
expect(results).to.deep.equal([
|
||||||
{ type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
{ line: lines[0], indent: ' ', type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
||||||
{ type: 1, level: 1, offsetHead: 1, offsetTail: 5 },
|
{ line: lines[1], indent: ' ', type: 1, level: 1, offsetHead: 1, offsetTail: 5 },
|
||||||
{ type: 1, level: 1, offsetHead: 1, offsetTail: 5 },
|
{ line: lines[2], indent: ' ', type: 1, level: 1, offsetHead: 1, offsetTail: 5 },
|
||||||
{ type: 1, level: 1, offsetHead: 1, offsetTail: 5 }
|
{ line: lines[3], indent: ' ', type: 1, level: 1, offsetHead: 1, offsetTail: 5 }
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -188,15 +188,16 @@ describe(`parseLine`, () => {
|
|||||||
''
|
''
|
||||||
]
|
]
|
||||||
|
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
const results = lines.map(line => {
|
const results = lines.map(line => {
|
||||||
parseLine(line, lineData)
|
lineData.line = line
|
||||||
|
parseLine(lineData)
|
||||||
return {...lineData}
|
return {...lineData}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).to.deep.equal([
|
expect(results).to.deep.equal([
|
||||||
{ type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
{ line: lines[0], indent: ' ', type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
||||||
{ type: 0, level: 1, offsetHead: 0, offsetTail: 0 }
|
{ line: lines[1], indent: ' ', type: 0, level: 1, offsetHead: 0, offsetTail: 0 }
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -208,24 +209,25 @@ describe(`parseLine`, () => {
|
|||||||
'',
|
'',
|
||||||
]
|
]
|
||||||
|
|
||||||
const lineData = LineData()
|
const lineData = createLineData()
|
||||||
const results = lines.map(line => {
|
const results = lines.map(line => {
|
||||||
parseLine(line, lineData)
|
lineData.line = line
|
||||||
|
parseLine(lineData)
|
||||||
return {...lineData}
|
return {...lineData}
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).to.deep.equal([
|
expect(results).to.deep.equal([
|
||||||
{ type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
{ line: lines[0], indent: ' ', type: 1, level: 0, offsetHead: 0, offsetTail: 4 },
|
||||||
{ type: 0, level: 1, offsetHead: 0, offsetTail: 0 },
|
{ line: lines[1], indent: ' ', type: 0, level: 1, offsetHead: 0, offsetTail: 0 },
|
||||||
{ type: 0, level: 1, offsetHead: 0, offsetTail: 0 },
|
{ line: lines[2], indent: ' ', type: 0, level: 1, offsetHead: 0, offsetTail: 0 },
|
||||||
{ type: 0, level: 1, offsetHead: 0, offsetTail: 0 }
|
{ line: lines[3], indent: ' ', type: 0, level: 1, offsetHead: 0, offsetTail: 0 }
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`Handle head and tail matching for lines with head and tail`, () => {
|
it(`Handle head and tail matching for lines with head and tail`, () => {
|
||||||
const line = ` head tail1 tail2 tail3`
|
const line = ` head tail1 tail2 tail3`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
|
|
||||||
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
|
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
|
||||||
const tail = line.slice(lineData.offsetTail + 1)
|
const tail = line.slice(lineData.offsetTail + 1)
|
||||||
@ -235,8 +237,8 @@ describe(`parseLine`, () => {
|
|||||||
|
|
||||||
it(`Handle head and tail matching for lines with head but no tail`, () => {
|
it(`Handle head and tail matching for lines with head but no tail`, () => {
|
||||||
const line = ` head`
|
const line = ` head`
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
|
|
||||||
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
|
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
|
||||||
const tail = line.slice(lineData.offsetTail + 1)
|
const tail = line.slice(lineData.offsetTail + 1)
|
||||||
@ -246,8 +248,8 @@ describe(`parseLine`, () => {
|
|||||||
|
|
||||||
it(`Handle head and tail matching for lines with head and trailing space`, () => {
|
it(`Handle head and tail matching for lines with head and trailing space`, () => {
|
||||||
const line = ` head `
|
const line = ` head `
|
||||||
const lineData = LineData()
|
const lineData = createLineData(line)
|
||||||
parseLine(line, lineData)
|
parseLine(lineData)
|
||||||
|
|
||||||
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
|
const head = line.slice(lineData.offsetHead, lineData.offsetTail)
|
||||||
const tail = line.slice(lineData.offsetTail + 1)
|
const tail = line.slice(lineData.offsetTail + 1)
|
||||||
|
@ -1,23 +1,25 @@
|
|||||||
type LineData = {
|
export type LineData = {
|
||||||
|
line: string|null;
|
||||||
|
indent: string;
|
||||||
type: number;
|
type: number;
|
||||||
level: number;
|
level: number;
|
||||||
offsetHead: number;
|
offsetHead: number;
|
||||||
offsetTail: number;
|
offsetTail: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function LineData(): LineData {
|
export function createLineData(line: string|null = null, indent: string = ' '): LineData {
|
||||||
return { type: 0, level: 0, offsetHead: 0, offsetTail: 0 }
|
return { line, indent, type: 0, level: 0, offsetHead: 0, offsetTail: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
export function parseLine(line: string, lineData: LineData, indent: string = ' '): LineData {
|
export function parseLine(lineData: LineData): LineData {
|
||||||
if (typeof line !== 'string') throw new Error(`'line' must be a string`)
|
if ((typeof lineData !== 'object' || !lineData) || typeof lineData.type !== 'number' || typeof lineData.level !== 'number') throw new Error(`'lineData' must be an object with 'line' string, and 'type' and 'level' integer properties`)
|
||||||
if ((typeof lineData !== 'object' || !lineData) || typeof lineData.type !== 'number' || typeof lineData.level !== 'number') throw new Error(`'lineData' must be an object with 'type' and 'level' integer properties`)
|
if (typeof lineData.indent !== 'string' || lineData.indent.length === 0 || lineData.indent.length > 1) throw new Error(`'lineData.indent' must be a single-character string`)
|
||||||
if (typeof indent !== 'string' || indent.length === 0 || indent.length > 1) throw new Error(`'indent' must be a single-character string`)
|
if (typeof lineData.line !== 'string') throw new Error(`'lineData.line' must be a string`)
|
||||||
|
|
||||||
let type = 0
|
let type = 0
|
||||||
let level = 0
|
let level = 0
|
||||||
|
|
||||||
if (!line.length) {
|
if (!lineData.line.length) {
|
||||||
if (lineData.type === 1) level += 1
|
if (lineData.type === 1) level += 1
|
||||||
if (lineData.type === 0) level = lineData.level
|
if (lineData.type === 0) level = lineData.level
|
||||||
|
|
||||||
@ -28,13 +30,13 @@ export function parseLine(line: string, lineData: LineData, indent: string = ' '
|
|||||||
} else {
|
} else {
|
||||||
type = 1
|
type = 1
|
||||||
|
|
||||||
while (line[level] === indent && level <= lineData.level + 1) ++level
|
while (lineData.line[level] === lineData.indent && level <= lineData.level + 1) ++level
|
||||||
lineData.type = type
|
lineData.type = type
|
||||||
lineData.level = level
|
lineData.level = level
|
||||||
lineData.offsetHead = level
|
lineData.offsetHead = level
|
||||||
lineData.offsetTail = level
|
lineData.offsetTail = level
|
||||||
|
|
||||||
while (line[lineData.offsetTail] && line[lineData.offsetTail] !== ' ') ++lineData.offsetTail
|
while (lineData.line[lineData.offsetTail] && lineData.line[lineData.offsetTail] !== ' ') ++lineData.offsetTail
|
||||||
}
|
}
|
||||||
|
|
||||||
return lineData
|
return lineData
|
||||||
|
17
packages/js/core/src/readers/js-string.ts
Normal file
17
packages/js/core/src/readers/js-string.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import type { Reader } from './reader'
|
||||||
|
|
||||||
|
export function createStringReader(doc: string|string[], index = 0): Reader {
|
||||||
|
const lines = Array.isArray(doc) ? doc : doc.split('\n')
|
||||||
|
|
||||||
|
const reader = {
|
||||||
|
index: index - 1,
|
||||||
|
next: () => {
|
||||||
|
reader.index++
|
||||||
|
if (reader.index >= lines.length) return null
|
||||||
|
return lines[reader.index]
|
||||||
|
},
|
||||||
|
clone: (startIndex?: number) => createStringReader(doc, startIndex == null ? index : reader.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
return reader
|
||||||
|
}
|
10
packages/js/core/src/readers/node-readline.ts
Normal file
10
packages/js/core/src/readers/node-readline.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import fs from 'node:fs'
|
||||||
|
import readline from 'node:readline/promises'
|
||||||
|
|
||||||
|
export function createReadlineReader(path: string): () => Promise<string|null> {
|
||||||
|
const it = readline.createInterface({
|
||||||
|
input: fs.createReadStream(path, 'utf-8'),
|
||||||
|
})[Symbol.asyncIterator]()
|
||||||
|
|
||||||
|
return async () => (await it.next()).value
|
||||||
|
}
|
5
packages/js/core/src/readers/reader.ts
Normal file
5
packages/js/core/src/readers/reader.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export type Reader = {
|
||||||
|
index: number,
|
||||||
|
next: () => string|null|Promise<string|null>,
|
||||||
|
clone: (startIndex?: number) => Reader
|
||||||
|
}
|
@ -1,16 +1,17 @@
|
|||||||
// vite.config.js
|
// vite.config.js
|
||||||
import { resolve } from 'path'
|
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig } from 'vite'
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
build: {
|
build: {
|
||||||
lib: {
|
lib: {
|
||||||
// Could also be a dictionary or array of multiple entry points
|
// Could also be a dictionary or array of multiple entry points
|
||||||
entry: [
|
entry: {
|
||||||
resolve(__dirname, 'src/index.ts'),
|
'index': 'src/index.ts',
|
||||||
resolve(__dirname, 'src/document.ts'),
|
'document': 'src/document.ts',
|
||||||
resolve(__dirname, 'src/parser.ts'),
|
'parser': 'src/parser.ts',
|
||||||
]
|
'readers/js-string': 'src/readers/js-string.ts',
|
||||||
|
'readers/node-readline': 'src/readers/node-readline.ts',
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user