Repository: danvk/literate-ts
Pull request #53 (2023-02-04 17:00:20)

Fixes for recent Node.js versions and Effective TypeScript
I ran the latest literate-ts against the _Effective TypeScript_ source for the first time in a while today and ran into some issues. This fixes them.

1. Newer versions of Node include a version number on stderr when they log a stack trace. This filters that out.
2. I put lots of fenced code blocks in comments in the _Effective TypeScript_ source. These weren't checked until #36. Most of them didn't pass the checker. Since these are mostly just "note to self" and not intended to be checked, I added an undocumented directive to disable checking them.


Patch:
diff --git a/examples/asciidoc/failures.asciidoc b/examples/asciidoc/failures.asciidoc
index 8f01e05..88ff29c 100644
--- a/examples/asciidoc/failures.asciidoc
+++ b/examples/asciidoc/failures.asciidoc
@@ -109,6 +109,22 @@ Actual:
 Actual output matched expected.
 ----
 
+You can also use this to check for (expected) Node.js runtime failures:
+
+[[toupper-js]]
+[source,js]
+----
+let city = 'new york city';
+console.log(city.toUppercase());
+----
+
+will throw an error when you run it:
+
+[[toupper-js-output]]
+----
+TypeError: city.toUppercase is not a function
+----
+
 === Bad replacements
 
 When you use the `-r` / `--replacements` flag, literate-ts will load additional code samples from that directory. These will be used in place of identically-named code samples in the text.
diff --git a/package.json b/package.json
index d0e0c6f..c189f33 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "literate-ts",
-  "version": "1.1.1",
+  "version": "1.2.0",
   "description": "Code samples that scale",
   "main": "dist/index.js",
   "repository": "https://github.com/danvk/literate-ts.git",
diff --git a/src/asciidoc.ts b/src/asciidoc.ts
index 7c722f5..e2f58de 100644
--- a/src/asciidoc.ts
+++ b/src/asciidoc.ts
@@ -8,6 +8,7 @@ const EXTRACT_DIRECTIVE = /^\/\/ verifier:(.*)$/;
 const TOP_HEADER = /^={1,3} (.*)$/;
 
 export function extractAsciidocSamples(text: string, p: Processor) {
+  let isIgnoringFencedCodeBlocks = false;
   const lines = text.split('\n');
 
   const extractCodeSample = (i: number, until: string) => {
@@ -37,12 +38,18 @@ export function extractAsciidocSamples(text: string, p: Processor) {
     } else if (header) {
       p.setHeader(header);
     } else if (directive) {
-      p.setDirective(directive);
+      if (directive === 'ignore-fenced-codeblocks') {
+        isIgnoringFencedCodeBlocks = true;
+      } else {
+        p.setDirective(directive);
+      }
     } else {
       const matches = matchAndExtract(EXTRACT_BACKTICKS, line);
       if (matches) {
-        p.setNextLanguage(matches);
-        i = extractCodeSample(i, '```');
+        if (!isIgnoringFencedCodeBlocks) {
+          p.setNextLanguage(matches);
+          i = extractCodeSample(i, '```');
+        }
       } else if (line === '----') {
         i = extractCodeSample(i, '----');
       }
diff --git a/src/main.ts b/src/main.ts
index 3be174b..c172959 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -92,7 +92,8 @@ function checkOutput(expectedOutput: string, input: CodeSample) {
   const tmpDir = getTempDir();
   const checkOutput = (actualOutput.stderr + actualOutput.stdout)
     .split('\n')
-    .filter(line => !line.startsWith('    at '))
+    .filter(line => !line.startsWith('    at ')) // prune stack traces to one line
+    .filter(line => !line.match(/^Node.js v\d+/)) // Newer versions of Node log a version number
     // Remove temp paths which vary from run to run.
     .map(line => (tmpDir ? line.replace('/private' + tmpDir, '').replace(tmpDir, '') : line))
     .join('\n')
@@ -111,6 +112,7 @@ function checkOutput(expectedOutput: string, input: CodeSample) {
     log('----');
     log('Actual:');
     log(checkOutput);
+    log('----');
   }
 }
 
@@ -120,7 +122,7 @@ async function checkSample(sample: CodeSample, idToSample: {[id: string]: CodeSa
   startSample(id);
 
   if (language === 'ts' || (language === 'js' && sample.checkJS)) {
-    await checkTs(content, sample, id + '-output' in idToSample, typeScriptBundle);
+    await checkTs(sample, id + '-output' in idToSample, typeScriptBundle);
   } else if (language === 'js') {
     // Run the sample through Node and record the output.
     const path = writeTempFile(id + '.js', content);
@@ -201,9 +203,7 @@ export function main() {
       console.log(file, `${numPassed}/${_.size(fileResults)} passed`);
       for (const [id, failures] of Object.entries(fileResults)) {
         numTotal += 1;
-        if (failures === 0) {
-          console.log(chalk.green(` ✓ ${id}`));
-        } else {
+        if (failures > 0) {
           console.log(chalk.red(` ✗ ${id}`));
           numFailures += 1;
         }


Test patch:
diff --git a/src/test/__snapshots__/asciidoc.test.ts.snap b/src/test/__snapshots__/asciidoc.test.ts.snap
index b4f8301..6c049e0 100644
--- a/src/test/__snapshots__/asciidoc.test.ts.snap
+++ b/src/test/__snapshots__/asciidoc.test.ts.snap
@@ -46,6 +46,8 @@ Array [
 ]
 `;
 
+exports[`extractSamples snapshot: backticks-disabled 1`] = `Array []`;
+
 exports[`extractSamples snapshot: commented-sample 1`] = `
 Array [
   Object {
diff --git a/src/test/inputs/backticks-disabled.asciidoc b/src/test/inputs/backticks-disabled.asciidoc
new file mode 100644
index 0000000..918a1ca
--- /dev/null
+++ b/src/test/inputs/backticks-disabled.asciidoc
@@ -0,0 +1,28 @@
+// verifier:ignore-fenced-codeblocks
+For instance, this is a valid TypeScript program:
+
+[[greet-ts]]
+```ts
+function greet(who: string) {
+  console.log('Hello', who);
+}
+```
+
+Also, we have tsx!
+
+[[greet-tsx]]
+```tsx
+export const Example = () => <div />;
+```
+
+But when you run this through a program like \`node\` that expects JavaScript, you'll get an error:
+
+```
+function greet(who: string) {
+                  ^
+
+SyntaxError: Unexpected token :
+```
+
+```ts
+console.log("end of file");
\ No newline at end of file