Skip to content

Commit

Permalink
fix: Lazy reading and async dumping of vfiles
Browse files Browse the repository at this point in the history
  • Loading branch information
nokome committed May 27, 2019
1 parent 4a0b411 commit d9e8e78
Show file tree
Hide file tree
Showing 14 changed files with 52 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/gdoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export async function parse(
file: VFile,
fetch: boolean = true
): Promise<stencila.Node> {
const json = dump(file)
const json = await dump(file)
const gdoc = JSON.parse(json)
return parseDocument(gdoc, fetch)
}
Expand Down
2 changes: 1 addition & 1 deletion src/html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export const mediaTypes = ['text/html']
* @returns A promise that resolves to a `stencila.Node`
*/
export async function parse(file: VFile): Promise<stencila.Node> {
const html = dump(file)
const html = await dump(file)
const dom = new jsdom.JSDOM(html)
const document = dom.window.document
collapse(document)
Expand Down
17 changes: 9 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,18 +299,19 @@ export async function write(
/**
* Convert content from one format to another.
*
* @param inp The input content (raw or file path).
* @param out The output file path.
* @param input The input content (raw or file path).
* @param outputPath The output file path.
* @param options Conversion options e.g `from` and `to`: to specify the formats to convert from/to
* @returns The converted content, or file path (for converters that only write to files).
*/
export async function convert(
inp: string,
out?: string,
input: string,
outputPath?: string,
options: { [key: string]: any } = {}
): Promise<string | undefined> {
const node = await read(inp, options.from)
let file = await unparse(node, out, options.to)
if (out) await vfile.write(file, out)
return file.contents ? vfile.dump(file) : file.path
const inputFile = vfile.create(input)
const node = await parse(inputFile, input, options.from)
const outputFile = await unparse(node, outputPath, options.to)
if (outputPath) await vfile.write(outputFile, outputPath)
return outputFile.contents ? vfile.dump(outputFile) : outputFile.path
}
2 changes: 1 addition & 1 deletion src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { dump, load, VFile } from './vfile'
export const mediaTypes = ['application/json']

export async function parse(file: VFile): Promise<stencila.Node> {
return JSON.parse(dump(file))
return JSON.parse(await dump(file))
}

export async function unparse(node: stencila.Node): Promise<VFile> {
Expand Down
2 changes: 1 addition & 1 deletion src/json5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const mediaTypes = ['application/json5']
* @returns A promise that resolves to a Stencila `Node`
*/
export async function parse(file: VFile): Promise<stencila.Node> {
return json5.parse(dump(file))
return json5.parse(await dump(file))
}

/**
Expand Down
10 changes: 6 additions & 4 deletions src/pandoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@ export async function parse(
ensureFile: boolean = false
): Promise<stencila.Node> {
const args = [`--from=${from}`, `--to=json`].concat(options)

let content = file.contents
if (!content || ensureFile) {
if (ensureFile && !file.path) throw new Error('Must supply a file')
args.push(`${file.path}`)
}

const json = await run(content, args)
const pdoc = JSON.parse(json)
return parseDocument(pdoc)
Expand Down Expand Up @@ -62,7 +64,6 @@ export async function unparse(
const pdoc = unparseArticle(node as stencila.Article)
await Promise.all(unparsePromises)

const json = JSON.stringify(pdoc)
const args = [`--from=json`, `--to=${to}`].concat(options)
if ((filePath && filePath !== '-') || ensureFile) {
let output
Expand All @@ -74,12 +75,13 @@ export async function unparse(
args.push(`--output=${output}`)
}

const json = JSON.stringify(pdoc)
const content = await run(json, args)

// If content was output, then load that into a vfile,
// otherwise the vfile, simply has path to the file created
// If content was outputted, then load that into a vfile,
// otherwise the vfile simply has path to the file created
if (content) return load(content)
else return create({ path: filePath })
else return create(undefined, { path: filePath })
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/tdp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const extNames = [
export async function parse(file: VFile): Promise<stencila.Node> {
let pkg: datapackage.Package
if (file.path) pkg = await datapackage.Package.load(file.path)
else pkg = await datapackage.Package.load(JSON.parse(dump(file)))
else pkg = await datapackage.Package.load(JSON.parse(await dump(file)))

// Parse resources
const parts = await Promise.all(pkg.resources.map(
Expand Down Expand Up @@ -199,7 +199,7 @@ async function unparseCreativeWork(
profile: 'tabular-data-resource',
name: datatable.name || 'Unnamed',

data: dump(await csv.unparse(datatable)),
data: await dump(await csv.unparse(datatable)),
format: 'csv',
mediatype: 'text/csv',
encoding: 'utf-8',
Expand Down
26 changes: 21 additions & 5 deletions src/vfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@ export type VFile = vfile.VFile
/**
* Create a virtual file
*
* @param options to `vfile` see https://github.com/vfile/vfile#vfileoptions
* @param contents Raw string contents of `VFile`, or file path
* @param options Options to `vfile` see https://github.com/vfile/vfile#vfileoptions
*/
export function create(options: any = {}): VFile {
export function create(
contents?: string,
options: { [key: string]: any } = {}
): VFile {
if (contents) {
if (isPath(contents)) options = { ...options, path: contents }
else options = { ...options, contents }
}
return vfile(options)
}

Expand All @@ -30,10 +38,18 @@ export function load(contents: VFileContents): VFile {
/**
* Dump a string from the contents of a virtual files
*
* If the file has no `contents` but does have a file
* `path`, then `read` the virtual file.
*
* @param file The virtual file to dump
*/
export function dump(vfile: VFile): string {
return vfile.contents ? vfile.contents.toString() : ''
export async function dump(vfile: VFile): Promise<string> {
if (vfile.contents) return vfile.toString()
if (vfile.path) {
const readFile = await toVFile.read(vfile.path)
return readFile.toString()
}
return ''
}

/**
Expand Down Expand Up @@ -66,7 +82,7 @@ export async function write(vfile: VFile, path: string): Promise<void> {
if (!path) throw new Error('Argument `path` is required')

if (path === '-') {
console.log(dump(vfile))
console.log(await dump(vfile))
} else if (path && vfile.contents) {
vfile.path = path
await toVFile.write(vfile)
Expand Down
2 changes: 1 addition & 1 deletion src/yaml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { dump, load, VFile } from './vfile'
export const mediaTypes = ['text/yaml']

export async function parse(file: VFile): Promise<stencila.Node> {
return yaml.safeLoad(dump(file))
return yaml.safeLoad(await dump(file))
}

export async function unparse(node: stencila.Node): Promise<VFile> {
Expand Down
6 changes: 3 additions & 3 deletions tests/csv.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ test('parse', async () => {
})

test('unparse', async () => {
expect(dump(await unparse(simple.node))).toEqual(simple.content)
expect(dump(await unparse(named.node))).toEqual(named.content)
expect(dump(await unparse(formulas.node))).toEqual(formulas.content)
expect(await dump(await unparse(simple.node))).toEqual(simple.content)
expect(await dump(await unparse(named.node))).toEqual(named.content)
expect(await dump(await unparse(formulas.node))).toEqual(formulas.content)
})
2 changes: 1 addition & 1 deletion tests/gdoc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ test('parse', async () => {
})

test('unparse', async () => {
const u = async (node: any) => JSON.parse(dump(await unparse(node)))
const u = async (node: any) => JSON.parse(await dump(await unparse(node)))
expect(await u(kitchenSink.node)).toEqual(kitchenSink.gdoc)
})

Expand Down
2 changes: 1 addition & 1 deletion tests/html.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ test('parse', async () => {
})

test('unparse', async () => {
expect(dump(await unparse(kitchenSink.node))).toEqual(kitchenSink.html)
expect(await dump(await unparse(kitchenSink.node))).toEqual(kitchenSink.html)
})

// An example intended for testing progressively added parser/unparser pairs
Expand Down
4 changes: 2 additions & 2 deletions tests/pandoc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test('parse', async () => {
})

test('unparse', async () => {
const u = async (node: any) => JSON.parse(dump(await unparse(node)))
const u = async (node: any) => JSON.parse(await dump(await unparse(node)))
expect(await u(kitchenSink.node)).toEqual(kitchenSink.pdoc)
})

Expand Down Expand Up @@ -566,7 +566,7 @@ test('rpngs', async () => {

expect(await parse(load(JSON.stringify(pdoc)))).toEqual(node)
// Skipping this until resolve how to deal with output RPNG paths
//expect(JSON.parse(dump(await unparse(node)))).toEqual(pdoc)
//expect(JSON.parse(await dump(await unparse(node)))).toEqual(pdoc)
})

// A very simple test of the approach to typing Pandoc nodes
Expand Down
4 changes: 2 additions & 2 deletions tests/tdp.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,11 @@ test('parse', async () => {
})

test('unparse', async () => {
const actual = JSON.parse(dump(await unparse(periodic.node)))
const actual = JSON.parse(await dump(await unparse(periodic.node)))
// Pretend that we unparsed to a a filePath i.e. that
// the data was written to disk.
delete actual.resources[0].data
actual.resources[0].path = 'periodic-table.csv'
const expected = JSON.parse(dump(await read(periodic.file)))
const expected = JSON.parse(await dump(await read(periodic.file)))
expect(actual).toEqual(expected)
})

0 comments on commit d9e8e78

Please sign in to comment.