Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

evaldown

Package Overview
Dependencies
Maintainers
1
Versions
27
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

evaldown - npm Package Compare versions

Comparing version 0.3.1 to 0.4.0

67

lib/cli.js

@@ -7,2 +7,14 @@ const fs = require("fs");

function validateIsFile(pathValue, pathKey) {
let maybeDir;
try {
maybeDir = fs.statSync(pathValue);
} catch (e) {
throw new Error(`Inaccessible "${pathKey}"`);
}
if (!maybeDir.isFile()) {
throw new Error(`Invalid "${pathKey}"`);
}
}
function validateIsDir(pathValue, pathKey) {

@@ -33,5 +45,34 @@ let maybeDir;

const byPathCmdValidators = {
file: validateIsFile,
files: validateIsDir
};
exports.byPath = async (pwd, opts) => {
const sourcePath = validatePath(pwd, opts._[0], "path");
let cmd = null;
for (const [name, validate] of Object.entries(byPathCmdValidators)) {
try {
validate(sourcePath);
cmd = name;
break;
} catch {}
}
if (cmd === null) {
throw new Error(`Invalid "path"`);
} else if (cmd === "files") {
opts.sourcePath = path.relative(pwd, sourcePath);
} else {
opts.sourcePath = ".";
}
return exports[cmd](pwd, opts);
};
exports.file = async (pwd, opts) => {
const cons = opts._cons || console;
const sourceFile = validatePath(pwd, opts._[0], "file");
validateIsFile(sourceFile, "sourceFile");

@@ -48,5 +89,6 @@ opts.outputFormat = opts.format;

const sourceRelativePath = path.relative(pwd, sourceFile);
if (opts.update) {
await evaldown.updateFile(sourceRelativePath);
} else {
if (opts.inplace || opts.update) {
await evaldown.processFile(sourceRelativePath);
}
if (!opts.inplace) {
const { targetOutput } = await evaldown.prepareFile(sourceRelativePath);

@@ -61,7 +103,16 @@ cons.log(targetOutput);

const targetPath = validatePathInOpts(pwd, opts, "targetPath");
// check that the parent directory exists
validateIsDir(path.join(targetPath, ".."), "targetPath");
// create the output directory if necessary
await fsExtra.ensureDir(targetPath);
let targetPath;
try {
targetPath = validatePathInOpts(pwd, opts, "targetPath");
// check that the parent directory exists
validateIsDir(path.join(targetPath, ".."), "targetPath");
// create the output directory if necessary
await fsExtra.ensureDir(targetPath);
} catch (e) {
if (opts.inplace) {
targetPath = null;
} else {
throw e;
}
}

@@ -68,0 +119,0 @@ opts.outputFormat = opts.format;

97

lib/Evaldown.js

@@ -16,4 +16,3 @@ const fs = require("fs").promises;

magicpenFormat: "html",
generateOutput: async (maker, evalOpts) =>
(await maker.withInlinedExamples(evalOpts)).toHtml()
generateOutput: async maker => (await maker.withInlinedExamples()).toHtml()
},

@@ -23,4 +22,3 @@ inlined: {

magicpenFormat: "html",
generateOutput: async (maker, evalOpts) =>
(await maker.withInlinedExamples(evalOpts)).toString()
generateOutput: async maker => (await maker.withInlinedExamples()).toText()
},

@@ -30,4 +28,3 @@ markdown: {

magicpenFormat: "text",
generateOutput: async (maker, evalOpts) =>
(await maker.withUpdatedExamples(evalOpts)).toString()
generateOutput: async maker => (await maker.withUpdatedExamples()).toText()
}

@@ -84,2 +81,5 @@ };

this.wrapper = wrapper;
// target handling
this.inplace = !!options.inplace;
this.update = !!options.update;

@@ -105,6 +105,9 @@

async makeOutputForContent(fileContent, format) {
const maker = new Markdown(fileContent, { marker: this.marker });
const maker = new Markdown(fileContent, {
marker: this.marker,
format: this.formatName
});
// set basic options for evaluation
const evalOpts = { capture: this.capture, format: this.formatName };
const evalOpts = { capture: this.capture };
// set globals to be attached if supplied

@@ -114,7 +117,9 @@ if (this.fileGlobals) {

}
// trigger evaluation for this bag of options
await maker.evaluate(evalOpts);
const targetOutput = await format.generateOutput(maker, evalOpts);
const targetOutput = await format.generateOutput(maker);
let sourceOutput;
if (!this.update) {
if (!(this.inplace || this.update)) {
sourceOutput = null;

@@ -125,3 +130,3 @@ } else if (this.formatName === "markdown") {

const markdownFormat = Evaldown.formats.markdown;
sourceOutput = await markdownFormat.generateOutput(maker, evalOpts);
sourceOutput = await markdownFormat.generateOutput(maker);
}

@@ -149,2 +154,3 @@

return {
sourceFile,
sourceBaseName,

@@ -158,35 +164,12 @@ sourceDirName,

async processFile(sourceFile) {
const {
sourceBaseName,
sourceDirName,
sourceFilePath,
sourceOutput,
targetOutput
} = await this.prepareFile(sourceFile);
const prepared = await this.prepareFile(sourceFile);
debug('processing source file "%s"', sourceFile);
const targetFile = path.join(
sourceDirName,
`${sourceBaseName}${this.targetExtension}`
);
const targetFilePath = path.join(this.targetPath, targetFile);
try {
await fsExtra.ensureDir(path.dirname(targetFilePath));
const context = {
sourceFile,
targetFile
};
await fs.writeFile(
targetFilePath,
this.wrapper(targetOutput, context),
"utf8"
);
} catch (e) {
throw new errors.TargetFileError(e);
if (!this.inplace && this.targetPath) {
await this.writeFile(prepared);
}
if (this.update) {
await fs.writeFile(sourceFilePath, sourceOutput, "utf8");
if (this.inplace || this.update) {
await this.updateFile(prepared);
}

@@ -222,4 +205,4 @@ }

async updateFile(sourceFile) {
const { sourceFilePath, sourceOutput } = await this.prepareFile(sourceFile);
async updateFile(prepared) {
const { sourceFile, sourceFilePath, sourceOutput } = prepared;

@@ -234,2 +217,34 @@ debug('updating source file "%s"', sourceFile);

}
async writeFile(prepared) {
const {
sourceFile,
sourceBaseName,
sourceDirName,
targetOutput
} = prepared;
debug('writing target for source file "%s"', sourceFile);
const targetFile = path.join(
sourceDirName,
`${sourceBaseName}${this.targetExtension}`
);
const targetFilePath = path.join(this.targetPath, targetFile);
try {
await fsExtra.ensureDir(path.dirname(targetFilePath));
const context = {
sourceFile,
targetFile
};
await fs.writeFile(
targetFilePath,
this.wrapper(targetOutput, context),
"utf8"
);
} catch (e) {
throw new errors.TargetFileError(e);
}
}
}

@@ -236,0 +251,0 @@

@@ -13,3 +13,2 @@ const symbols = {

return function(first, ...rest) {
if (!first) first = "";
if (typeof first === "string") {

@@ -16,0 +15,0 @@ this[symbols.line](out, first);

@@ -39,9 +39,7 @@ var vm = require("vm");

function getOutputString(expect, format, output) {
if (typeof output === "string") {
return output;
} else if (output) {
function getOutputString(expect, format, flags, output) {
if (typeof output === "undefined" && format !== "markdown" && flags.return) {
return `<div style="">&nbsp;</div>`;
} else {
return expect.inspect(output, 6, format).toString();
} else {
return "";
}

@@ -135,3 +133,5 @@ }

const injectedCode = transpiledCode[0];
const allBlocks = transpiledCode.split(separatorRegexp);
const injectedCode = transpiledCode[0].trimRight();
if (injectedCode) {

@@ -142,3 +142,3 @@ vm.runInThisContext(injectedCode);

// unpack the transpiled code block for each snippet
const transpiledBlocks = transpiledCode.split(separatorRegexp).slice(1);
const transpiledBlocks = allBlocks.slice(1);

@@ -195,4 +195,4 @@ for (const [i, transpiledSnippet] of transpiledBlocks.entries()) {

output.kind = "result";
output.html = getOutputString(expectForOutput, "html", result);
output.text = getOutputString(expectForOutput, "text", result);
output.html = getOutputString(expectForOutput, "html", flags, result);
output.text = getOutputString(expectForOutput, "text", flags, result);
} else if (global.console instanceof InspectedConsole) {

@@ -199,0 +199,0 @@ if (global.console[consoleSymbols.isEmpty]()) return output;

@@ -97,2 +97,3 @@ const snippetRegexp = require("./snippetRegexp");

codeIndexEnd: -1,
comment: comment || "",
output: null

@@ -99,0 +100,0 @@ };

var Snippets = require("./Snippets");
var cleanStackTrace = require("./cleanStackTrace");
var createExpect = require("./createExpect");
var snippetRegexp = require("./snippetRegexp");
var marked = require("marked-papandreou");

@@ -104,3 +103,4 @@

async evaluateSnippets(options) {
async evaluate(options) {
options = this._prepareOptions(options);
const globals = this._prepareGlobals(options);

@@ -111,3 +111,2 @@ const evalOpts = { markdown: this, globals, ...options };

await snippets.evaluate(evalOpts);
return snippets;
}

@@ -120,9 +119,11 @@

toString() {
toText() {
return this.content;
}
async withInlinedExamples(options) {
options = this._prepareOptions(options);
const snippets = await this.evaluateSnippets(options);
async withExamples(onSnippet) {
const snippets = this.snippets;
if (!(snippets && snippets.evaluated)) {
throw new Error("snippets were not evaluated");
}

@@ -132,6 +133,28 @@ let updatedContent = this.content.slice(0);

for (const [index, snippet] of snippets.entries()) {
const { code, lang } = snippet;
const blockLength = snippet.indexEnd - snippet.index;
const blockRendered = onSnippet(snippet, snippets, index);
if (blockRendered === null) {
continue;
}
updatedContent = spliceString(
updatedContent,
snippet.index + blockDelta,
snippet.indexEnd + blockDelta,
blockRendered
);
blockDelta += blockRendered.length - blockLength;
}
return updatedContent;
}
async withInlinedExamples() {
const onSnippet = (snippet, snippets, index) => {
const { code, lang } = snippet;
let blockRendered;
if (lang === "output") {

@@ -159,3 +182,3 @@ // the presence of the matching previous snippet

if (options.format === "inlined") {
if (this.options.format === "inlined") {
blockRendered = blockRendered.replace(/^<div/, "<pre"); // start of the block

@@ -165,58 +188,40 @@ blockRendered = blockRendered.replace(/\/div>$/, "/pre>"); // end of the block

updatedContent = spliceString(
updatedContent,
snippet.index + blockDelta,
snippet.indexEnd + blockDelta,
blockRendered
);
return blockRendered;
};
blockDelta += blockRendered.length - blockLength;
}
const updatedContent = await this.withExamples(onSnippet);
return new Markdown(updatedContent, this.options);
}
async withUpdatedExamples(options) {
options = this._prepareOptions(options);
const snippets = await this.evaluateSnippets(options);
async withUpdatedExamples() {
const onSnippet = (snippet, snippets, index) => {
const { code, lang, comment } = snippet;
let index = 0;
const updatedContent = this.content.replace(
snippetRegexp,
(block, htmlComments, lang) => {
var currentIndex = index;
index += 1;
var snippet = snippets.get(currentIndex);
if (snippet.lang === "output") {
// the presence of the matching previous snippet
// is gauranteed when we evaluate the snippets
var example = snippets.get(currentIndex - 1);
if (lang === "output") {
// the presence of the matching previous snippet
// is gauranteed when we evaluate the snippets
var example = snippets.get(index - 1);
var output = "";
if (example.output) {
output = example.output.text;
if (
example.output.kind === "error" &&
snippet.flags.cleanStackTrace
) {
output = cleanStackTrace(output);
}
var output = "";
if (example.output) {
output = example.output.text;
if (
example.output.kind === "error" &&
snippet.flags.cleanStackTrace
) {
output = cleanStackTrace(output);
}
}
return `${htmlComments || ""}\`\`\`${lang}\n${output}\n\`\`\``;
} else if (snippet.includesLegacyFlags) {
const langFlagsIndex = lang ? lang.indexOf("#") : -1;
let htmlComments;
if (langFlagsIndex > -1) {
lang = lang.slice(0, langFlagsIndex);
htmlComments = flagsToHtmlComment(this.marker, snippet.flags);
htmlComments += "\n";
}
return `${htmlComments || ""}\`\`\`${lang}\n${snippet.code}\n\`\`\``;
} else {
return block;
}
return `${comment || ""}\`\`\`${lang}\n${output}\n\`\`\``;
} else if (snippet.includesLegacyFlags) {
let htmlComments = flagsToHtmlComment(this.marker, snippet.flags);
if (htmlComments.length > 0) htmlComments += "\n";
return `${htmlComments}\`\`\`${lang}\n${code}\n\`\`\``;
} else {
return null;
}
);
};
const updatedContent = await this.withExamples(onSnippet);
return new Markdown(updatedContent, this.options);

@@ -223,0 +228,0 @@ }

@@ -6,2 +6,3 @@ var extractSnippets = require("./extractSnippets");

constructor(snippets) {
this.evaluated = false;
this.items = snippets;

@@ -31,4 +32,7 @@ }

async evaluate(options) {
if (this.evaluated) {
throw new Error("the same snippets were evaluated twice");
}
await evaluateSnippets(this.snippets, options);
return this;
this.evaluated = true;
}

@@ -35,0 +39,0 @@

{
"name": "evaldown",
"version": "0.3.1",
"version": "0.4.0",
"description": "Evalute JavaScript snippets in markdown files and output static pages.",

@@ -5,0 +5,0 @@ "main": "lib/Evaldown.js",

# Evaldown
Evalute JavaScript snippets in markdown files and output static pages.
Evaluate JavaScript snippets in markdown files.

@@ -9,21 +9,22 @@ [![NPM version](https://img.shields.io/npm/v/evaldown.svg)](https://www.npmjs.com/package/evaldown)

This project will recursively traverse a folder structure searching
for markdown files. Once found, it will extract javascript code blocks,
evaluate them and serialise their pretty-printed output for rendering.
This tool provides both CLI and programmatic interfaces for
locating JavaScript code blocks in one or more markdown files,
extracting and evaluating these blocks and provides a range
formats in which to serialise their pretty-printed output.
The tool can even automatically capture the output of your examples.
See the [updating](#Updating-examples) section for more details.
## Use
Once the tool is installed and configured it can be used via the CLI.
which supports processing a single markdown file or more typically
a directory of files with configuration read from a file.
We start by introducing an invocation for processing a single
markdown file:
### Process single files
```
./node_modules/.bin/evaldown ./docs/README.md
```
In single file use, the tool can be invoked as follows:
The file will be processed and the output written to stdout.
In order to store the output within the source file, thereby
automatically capturing it, we can use the `--inplace` option:
```
./node_modules/.bin/evaldown ./docs/README.md > README.md
./node_modules/.bin/evaldown --inplace ./docs/README.md
```

@@ -33,12 +34,20 @@

This mode requires a configuration file. Once written, the tool
is invoked will read the source path, scan it for markdown files
and write output to a target path. This mode is invoked by:
Applying a similiar update to all files within a directory
structure looks almost identical:
```
./node_modules/.bin/evaldown --inplace ./testdata/
```
### Beyond command line options
The tool supports many additional options to alter its behaviour,
and these can all be read directly from a configuration file:
```
./node_modules/.bin/evaldown --config <path_to_config>
```
The sections below discuss configuring the the tool and
authoring you first example files.
The sections below discuss configuring the tool and
authoring of examples.

@@ -117,2 +126,14 @@ ## Configuration

### Keeping the source up-to-date
As you change your examples, updating means you can always keep the
output up-to-date. This mode is considered a key use-case and can
enabled by default via the configuration file:
It can also be activaited on the command line on demand:
```
./node_modules/.bin/evaldown --config <path_to_config> --update
```
## Authoring

@@ -182,26 +203,1 @@

</pre>
## Updating examples
Rather than be forced to write the output by hand, we can automatially
execute the provided code snippets and inject their results into the
source markdown files. This is done using the `"update"` option.
As you change your examples, updating means you can always keep the
output up-to-date. This mode is considered a _primary use-case_ and
can be activated by supplying an additional command line option:
```
./node_modules/.bin/evaldown --config <path_to_config> --update
```
It can also be placed within the configuration file:
```js
module.exports = {
update: true,
sourcePath: "./input",
targetPath: "./output"
};
```

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc