diff --git a/packages/js/.gitignore b/packages/js/.gitignore index 600e365..3540683 100644 --- a/packages/js/.gitignore +++ b/packages/js/.gitignore @@ -1 +1,2 @@ -**/node_modules \ No newline at end of file +**/node_modules +dist/ \ No newline at end of file diff --git a/packages/js/core/LICENSE.md b/packages/js/LICENSE.md similarity index 100% rename from packages/js/core/LICENSE.md rename to packages/js/LICENSE.md diff --git a/packages/js/core/dist/document.cjs b/packages/js/core/dist/document.cjs deleted file mode 100644 index 1cee5b5..0000000 --- a/packages/js/core/dist/document.cjs +++ /dev/null @@ -1 +0,0 @@ -"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const i=require("./parser.cjs");function f(r,c=" "){const e=i.createLineData("",c);let n=!1;async function o(t=-1){if(n)n=!1;else{const a=await r();if(a==null)return!1;e.line=a,i.parseLine(e)}return l()<=t?(n=!0,!1):!0}const l=()=>e.level,u=(t=e.offsetHead)=>e.line.slice(t),s=()=>e.line.slice(e.offsetHead,e.offsetTail);return{next:o,level:l,line:u,head:s,tail:()=>e.line.slice(e.offsetTail),match:t=>t===s()}}exports.useDocument=f; diff --git a/packages/js/core/dist/document.js b/packages/js/core/dist/document.js deleted file mode 100644 index df07341..0000000 --- a/packages/js/core/dist/document.js +++ /dev/null @@ -1,28 +0,0 @@ -import { parseLine as r, createLineData as u } from "./parser.js"; -function h(s, f = " ") { - const e = u("", f); - let n = !1; - async function c(t = -1) { - if (n) - n = !1; - else { - const i = await s(); - if (i == null) - return !1; - e.line = i, r(e); - } - return l() <= t ? (n = !0, !1) : !0; - } - const l = () => e.level, o = (t = e.offsetHead) => e.line.slice(t), a = () => e.line.slice(e.offsetHead, e.offsetTail); - return { - next: c, - level: l, - line: o, - head: a, - tail: () => e.line.slice(e.offsetTail), - match: (t) => t === a() - }; -} -export { - h as useDocument -}; diff --git a/packages/js/core/dist/index.cjs b/packages/js/core/dist/index.cjs deleted file mode 100644 index 456ad3b..0000000 --- a/packages/js/core/dist/index.cjs +++ /dev/null @@ -1 +0,0 @@ -"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; diff --git a/packages/js/core/dist/index.js b/packages/js/core/dist/index.js deleted file mode 100644 index d0a9970..0000000 --- a/packages/js/core/dist/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import { createLineData as o, parseLine as t } from "./parser.js"; -import { useDocument as m } from "./document.js"; -export { - o as createLineData, - t as parseLine, - m as useDocument -}; diff --git a/packages/js/core/dist/parser.cjs b/packages/js/core/dist/parser.cjs deleted file mode 100644 index d375452..0000000 --- a/packages/js/core/dist/parser.cjs +++ /dev/null @@ -1 +0,0 @@ -"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function t(e="",r=" "){return{line:e,indent:r,level:0,offsetHead:0,offsetTail:0}}function f(e){if(typeof e!="object"||!e||typeof e.level!="number")throw new Error("'lineData' must be an object with string line and numeric level 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;if(!e.line.length)e.level=e.level,e.offsetHead=0,e.offsetTail=0;else{for(;e.line[r]===e.indent&&r<=e.level+1;)++r;for(e.level=r,e.offsetHead=r,e.offsetTail=r;e.line[e.offsetTail]&&e.line[e.offsetTail]!==" ";)++e.offsetTail}return e}exports.createLineData=t;exports.parseLine=f; diff --git a/packages/js/core/dist/parser.js b/packages/js/core/dist/parser.js deleted file mode 100644 index 23141e1..0000000 --- a/packages/js/core/dist/parser.js +++ /dev/null @@ -1,25 +0,0 @@ -function f(e = "", r = " ") { - return { line: e, indent: r, level: 0, offsetHead: 0, offsetTail: 0 }; -} -function o(e) { - if (typeof e != "object" || !e || typeof e.level != "number") - throw new Error("'lineData' must be an object with string line and numeric level 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; - if (!e.line.length) - e.level = e.level, e.offsetHead = 0, e.offsetTail = 0; - else { - for (; e.line[r] === e.indent && r <= e.level + 1; ) - ++r; - for (e.level = r, e.offsetHead = r, e.offsetTail = r; e.line[e.offsetTail] && e.line[e.offsetTail] !== " "; ) - ++e.offsetTail; - } - return e; -} -export { - f as createLineData, - o as parseLine -}; diff --git a/packages/js/core/dist/readers/js-string.cjs b/packages/js/core/dist/readers/js-string.cjs deleted file mode 100644 index 6963947..0000000 --- a/packages/js/core/dist/readers/js-string.cjs +++ /dev/null @@ -1,2 +0,0 @@ -"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});function n(r,e=0){const t=Array.isArray(r)?r:r.split(` -`);return e--,()=>(e++,e>=t.length?null:t[e])}exports.createStringReader=n; diff --git a/packages/js/core/dist/readers/js-string.js b/packages/js/core/dist/readers/js-string.js deleted file mode 100644 index 34d7fd5..0000000 --- a/packages/js/core/dist/readers/js-string.js +++ /dev/null @@ -1,8 +0,0 @@ -function e(t, r = 0) { - const n = Array.isArray(t) ? t : t.split(` -`); - return r--, () => (r++, r >= n.length ? null : n[r]); -} -export { - e as createStringReader -}; diff --git a/packages/js/core/dist/readers/node-readline.cjs b/packages/js/core/dist/readers/node-readline.cjs deleted file mode 100644 index 6c2f6bf..0000000 --- a/packages/js/core/dist/readers/node-readline.cjs +++ /dev/null @@ -1 +0,0 @@ -"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const r=require("node:fs"),n=require("node:readline/promises"),t=e=>e&&typeof e=="object"&&"default"in e?e:{default:e},o=t(r),u=t(n);function d(e){const a=u.default.createInterface({input:o.default.createReadStream(e,"utf-8")})[Symbol.asyncIterator]();return async()=>(await a.next()).value}exports.createReadlineReader=d; diff --git a/packages/js/core/dist/readers/node-readline.js b/packages/js/core/dist/readers/node-readline.js deleted file mode 100644 index 303e70f..0000000 --- a/packages/js/core/dist/readers/node-readline.js +++ /dev/null @@ -1,11 +0,0 @@ -import r from "node:fs"; -import a from "node:readline/promises"; -function c(e) { - const t = a.createInterface({ - input: r.createReadStream(e, "utf-8") - })[Symbol.asyncIterator](); - return async () => (await t.next()).value; -} -export { - c as createReadlineReader -}; diff --git a/packages/js/core/package-lock.json b/packages/js/package-lock.json similarity index 100% rename from packages/js/core/package-lock.json rename to packages/js/package-lock.json diff --git a/packages/js/core/package.json b/packages/js/package.json similarity index 95% rename from packages/js/core/package.json rename to packages/js/package.json index a8a5568..6697c8d 100644 --- a/packages/js/core/package.json +++ b/packages/js/package.json @@ -26,7 +26,7 @@ } }, "scripts": { - "test": "vitest ./src", + "test": "node ./test/index.js", "dev": "vite build --watch", "build": "vite build" }, diff --git a/packages/js/core/src/document.ts b/packages/js/src/document.ts similarity index 95% rename from packages/js/core/src/document.ts rename to packages/js/src/document.ts index fcdd728..6fd60b5 100644 --- a/packages/js/core/src/document.ts +++ b/packages/js/src/document.ts @@ -42,7 +42,7 @@ export function useDocument (reader: Reader, indent: string = ' '): Document { const level = () => lineData.level const line = (startOffset: number = lineData.offsetHead) => lineData.line.slice(startOffset) const head = () => lineData.line.slice(lineData.offsetHead, lineData.offsetTail) - const tail = () => lineData.line.slice(lineData.offsetTail) + const tail = () => lineData.line.slice(lineData.offsetTail + 1) // Skip the space const match = (matchHead: string): boolean => matchHead === head() return { diff --git a/packages/js/core/src/index.ts b/packages/js/src/index.ts similarity index 100% rename from packages/js/core/src/index.ts rename to packages/js/src/index.ts diff --git a/packages/js/core/src/parser.test.ts b/packages/js/src/parser.test.ts similarity index 100% rename from packages/js/core/src/parser.test.ts rename to packages/js/src/parser.test.ts diff --git a/packages/js/core/src/parser.ts b/packages/js/src/parser.ts similarity index 100% rename from packages/js/core/src/parser.ts rename to packages/js/src/parser.ts diff --git a/packages/js/core/src/readers/js-string.ts b/packages/js/src/readers/js-string.ts similarity index 100% rename from packages/js/core/src/readers/js-string.ts rename to packages/js/src/readers/js-string.ts diff --git a/packages/js/core/src/readers/node-readline.ts b/packages/js/src/readers/node-readline.ts similarity index 52% rename from packages/js/core/src/readers/node-readline.ts rename to packages/js/src/readers/node-readline.ts index 5aebba8..99c5191 100644 --- a/packages/js/core/src/readers/node-readline.ts +++ b/packages/js/src/readers/node-readline.ts @@ -2,10 +2,18 @@ import fs from 'node:fs' import readline from 'node:readline/promises' import type { Reader } from './reader' -export function createReadlineReader(path: string): Reader { +export function createFileReader(path: string): Reader { const it = readline.createInterface({ input: fs.createReadStream(path, 'utf-8'), })[Symbol.asyncIterator]() return async () => (await it.next()).value } + +export function createStdinReader(): Reader { + const it = readline.createInterface({ + input: process.stdin + })[Symbol.asyncIterator]() + + return async () => (await it.next()).value +} diff --git a/packages/js/core/src/readers/reader.ts b/packages/js/src/readers/reader.ts similarity index 100% rename from packages/js/core/src/readers/reader.ts rename to packages/js/src/readers/reader.ts diff --git a/packages/js/test/index.js b/packages/js/test/index.js new file mode 100644 index 0000000..5459686 --- /dev/null +++ b/packages/js/test/index.js @@ -0,0 +1,23 @@ +import { createLineData, parseLine, useDocument } from '@terrace/core' +import { createStdinReader } from '@terrace/core/readers/node-readline' + +const testName = process.argv[2] + + +const tests = { + 'linedata:basic': async () => { + const { level, line, head, tail, next } = useDocument(createStdinReader()) + while(await next()) { + console.log(`level: ${level()} | head: ${head()} | tail: ${tail()} | line: ${line()}`) + } + }, + 'linedata:tabs': async () => { + const { level, line, head, tail, next } = useDocument(createStdinReader(), '\t') + while(await next()) { + console.log(`level: ${level()} | head: ${head()} | tail: ${tail()} | line: ${line()}`) + } + } +} + +const test = tests[testName] +await test() \ No newline at end of file diff --git a/packages/js/core/tsconfig.json b/packages/js/tsconfig.json similarity index 100% rename from packages/js/core/tsconfig.json rename to packages/js/tsconfig.json diff --git a/packages/js/core/vite.config.js b/packages/js/vite.config.js similarity index 100% rename from packages/js/core/vite.config.js rename to packages/js/vite.config.js diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2a7ff3..539d574 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,11 +8,11 @@ importers: jest: ^29.4.1 turbo: ^1.7.3 devDependencies: - '@terrace/core': link:packages/js/core + '@terrace/core': link:packages/js jest: 29.4.1 turbo: 1.7.3 - packages/js/core: + packages/js: specifiers: vite: ^3.2.3 vitest: ^0.24.5 @@ -24,7 +24,7 @@ importers: specifiers: '@terrace/core': workspace:* dependencies: - '@terrace/core': link:../packages/js/core + '@terrace/core': link:../packages/js packages: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f377811..53d3481 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,4 +1,4 @@ packages: - "./test" - - "./packages/js/*" + - "./packages/*" - "./experiments/*" diff --git a/test/package.json b/test/package.json index a396e46..42d5a40 100644 --- a/test/package.json +++ b/test/package.json @@ -2,7 +2,7 @@ "name": "@terrace/test", "type": "module", "scripts": { - "test": "NODE_OPTIONS=--experimental-vm-modules jest" + "test": "NODE_OPTIONS=--experimental-vm-modules NODE_NO_WARNINGS=1 jest" }, "dependencies": { "@terrace/core": "workspace:*" diff --git a/test/test-runner.test.js b/test/test-runner.test.js index 84c0473..f494a6f 100644 --- a/test/test-runner.test.js +++ b/test/test-runner.test.js @@ -1,43 +1,83 @@ import { expect } from 'chai' import fs from 'node:fs/promises' +import { execSync } from 'node:child_process' import { useDocument } from '@terrace/core' -import { createReadlineReader } from '@terrace/core/readers/node-readline' +import { createFileReader } from '@terrace/core/readers/node-readline' async function loadTests(path) { - const { next, level, head, tail, line, match } = useDocument(createReadlineReader(path)) + const { next, level, head, tail, line, match } = useDocument(createFileReader(path)) - const tests = {} + const descriptions = {} while (await next()) { if (!head() || match('#schema')) continue + const tests = descriptions[tail()] = [] + const rootLevel = level() - - const test = tests[tail().trim()] = { input: [], output: [] } - while (await next(rootLevel)) { - const testLevel = level() - if (match('input')) { - while (await next(testLevel)) { - test.input.push(line(testLevel)) - } - } + if (!match('it')) continue - if (match('output')) { - while (await next(testLevel)) { - test.output.push(line(testLevel)) + const test = { it: tail(), packages: [], key: '', input: [], output: [] } + tests.push(test) + + const testLevel = level() + while (await next(testLevel)) { + if (match('key')) test.key = tail() + if (match('packages')) test.packages = tail().split(' ') + + const propertyLevel = level() + if (match('input')) { + // TODO: Find a way to handle newlines better. + if (tail().startsWith('literal')) { + test.input = tail() + .split('literal ').join('') + .replaceAll('\\n', '\n') + .replaceAll('\\t', '\t') + continue + } + + while (await next(propertyLevel)) test.input.push(line(propertyLevel)) + test.input = test.input.join('\n').trim() + } + + if (match('output')) { + // TODO: Find a way to handle newlines better. + if (tail().startsWith('literal')) { + test.output = tail().split('literal ').join('').replace('\\n', '\n') + continue + } + + while (await next(propertyLevel)) test.output.push(line()) } } + test.output = test.output.join('\n').trim() } } - - return tests + return descriptions } -it('Runs a basic test', async () => { - const tests = await loadTests('./tests.tce', 'utf-8') +function callTest(pkg, name, input) { + return new Promise((resolve, reject) => { + const stdout = execSync(`npm run --silent test ${name}`, { + cwd: `../packages/${pkg}`, + input + }) - console.log(tests) + resolve(stdout.toString().trim()) + }) +} - expect(1).to.equal(1) -}) +const descriptions = await loadTests('./tests.tce') + +for (const [name, tests] of Object.entries(descriptions)) { + describe(name, () => { + for (const test of tests) { + test.packages.forEach(pkg => { + it(`${pkg}: ${test.it}`, async () => { + expect(await callTest(pkg, test.key, test.input)).to.equal(test.output) + }) + }) + } + }) +} \ No newline at end of file diff --git a/test/tests.tce b/test/tests.tce index 0d2addb..04f46a5 100644 --- a/test/tests.tce +++ b/test/tests.tce @@ -1,8 +1,54 @@ #schema test -test Basic - input - key value +describe LineData + it Handles a blank line at indent level 0 + key linedata:basic + packages js + input literal \n + output + level: 0 | head: | tail: | line: - output - level: 0 | head: key | tail: value | line: key value + it Handles a blank line with a single space at indent level 1 + key linedata:basic + packages js + input literal \n + output + level: 1 | head: | tail: | line: + + it Handles a blank line with two spaces + key linedata:basic + packages js + input literal \n + output + level: 2 | head: | tail: | line: + + it Handles a normal line at indent level 1 + key linedata:basic + packages js + input literal line 1 + output + level: 1 | head: line | tail: 1 | line: line 1 + + it Handles a normal line at indent level 1 indented with tabs + key linedata:tabs + packages js + input literal \tline 1 + output + level: 1 | head: line | tail: 1 | line: line 1 + + it Handles a normal line at indent level 2 indented with tabs + key linedata:tabs + packages js + input literal \t\tline 1 + output + level: 2 | head: line | tail: 1 | line: line 1 + + it Nests a normal line under a preceding normal line + key linedata:basic + packages js + input + line 1 + line 2 + output + level: 0 | head: line | tail: 1 | line: line 1 + level: 1 | head: line | tail: 2 | line: line 2