Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ by adding `@nolint` to the info string.

`lint-roller-markdown-ts-check` is a command to type check JS/TS code blocks
in Markdown with `tsc`. Type checking can be disabled for specific code blocks
by adding `@ts-nocheck` to the info string, specific lines can be ignored
by adding `@ts-ignore=[<line1>,<line2>]` to the info string, and additional
by adding `@ts-nocheck` to the info string, specific lines can be ignored by
adding `@ts-expect-error=[<line1>,<line2>]` to the info string, and additional
globals can be defined with `@ts-type={name:type}`. The `Window` object can
be extended with more types using `@ts-window-type={name:type}`. When type
checking TypeScript blocks in the same Markdown file, global augmentation
Expand Down
49 changes: 11 additions & 38 deletions bin/lint-markdown-ts-check.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ async function typeCheckFiles(
'',
);

// Strip any @ts-expect-error/@ts-ignore comments we added
correctedOutput = correctedOutput.replace(/ \/\/ @ts-(?:expect-error|ignore)/g, '');
// Strip any @ts-expect-error comments we added
correctedOutput = correctedOutput.replace(/ \/\/ @ts-expect-error/g, '');

if (correctedOutput.trim()) {
for (const [filename, originalFilename] of filenameMapping.entries()) {
Expand Down Expand Up @@ -110,38 +110,16 @@ async function main(
?.match(/\B@ts-expect-error=\[([\d,]*)\]\B/)?.[1]
.split(',')
.map((line) => parseInt(line));
const tsIgnoreLines = codeBlock.meta
?.match(/\B@ts-ignore=\[([\d,]*)\]\B/)?.[1]
.split(',')
.map((line) => parseInt(line));
const tsTypeLines = codeBlock.meta ? parseDirectives('@ts-type', codeBlock.meta) : [];
const tsWindowTypeLines = codeBlock.meta
? parseDirectives('@ts-window-type', codeBlock.meta)
: [];

if (
tsNoCheck &&
(tsExpectErrorLines || tsIgnoreLines || tsTypeLines.length || tsWindowTypeLines.length)
) {
console.log(
`${filepath}:${line}:${
indent + 1
}: Code block has both @ts-nocheck and @ts-expect-error/@ts-ignore/@ts-type/@ts-window-type, they conflict`,
);
errors = true;
continue;
}

// There should be no overlap between @ts-expect-error and @ts-ignore
if (
tsExpectErrorLines &&
tsIgnoreLines &&
tsExpectErrorLines.some((line) => tsIgnoreLines.includes(line))
) {
if (tsNoCheck && (tsExpectErrorLines || tsTypeLines.length || tsWindowTypeLines.length)) {
console.log(
`${filepath}:${line}:${
indent + 1
}: Code block has both @ts-expect-error and @ts-ignore with same line number(s)`,
}: Code block has both @ts-nocheck and @ts-expect-error/@ts-type/@ts-window-type, they conflict`,
);
errors = true;
continue;
Expand All @@ -165,11 +143,11 @@ async function main(
const insertComment = (comment: string, line: number) => {
// Inserting additional lines will make the tsc output
// incorrect which would be a pain to manually adjust,
// and there is no @ts-ignore-line, so tack the comment
// on to the end of the previous line - looks ugly but
// we never have to see it since it's in a temp file.
// The first line of the file is an edge case where an
// insertion is necessary, so take that into account
// and there is no @ts-expect-error-line, so tack the
// comment on to the end of the previous line - looks
// ugly but we never have to see it since it's in a temp
// file. The first line of the file is an edge case where
// an insertion is necessary, so take that into account
if (line === 1) {
codeLines.unshift(comment);
insertedInitialLine = true;
Expand All @@ -186,15 +164,10 @@ async function main(
}
};

// Blocks can have @ts-ignore=[1,10,50] in their info string
// (1-based lines) to insert an "// @ts-ignore" comment before
// Blocks can have @ts-expect-error=[1,10,50] in their info string
// (1-based lines) to insert an "// @ts-expect-error" comment before
// specified lines, in order to ignore specific lines (like
// requires of extra modules) without skipping the whole block
for (const line of tsIgnoreLines ?? []) {
insertComment('// @ts-ignore', line);
}

// Blocks can also have @ts-expect-error
for (const line of tsExpectErrorLines ?? []) {
insertComment('// @ts-expect-error', line);
}
Expand Down
70 changes: 32 additions & 38 deletions tests/__snapshots__/lint-roller-markdown-ts-check.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -1,92 +1,86 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`lint-roller-markdown-ts-check should type check code blocks 1`] = `
"ts-check.md:49:1: Code block has both @ts-nocheck and @ts-expect-error/@ts-ignore/@ts-type/@ts-window-type, they conflict
ts-check.md:55:1: Code block has both @ts-nocheck and @ts-expect-error/@ts-ignore/@ts-type/@ts-window-type, they conflict
ts-check.md:128:8 - error TS2339: Property 'myAwesomeAPI' does not exist on type 'Window & typeof globalThis'.
"ts-check.md:37:1: Code block has both @ts-nocheck and @ts-expect-error/@ts-type/@ts-window-type, they conflict
ts-check.md:43:1: Code block has both @ts-nocheck and @ts-expect-error/@ts-type/@ts-window-type, they conflict
ts-check.md:109:5 - error TS2304: Cannot find name 'a'.

128 window.myAwesomeAPI()
   ~~~~~~~~~~~~

ts-check.md:154:5 - error TS2304: Cannot find name 'a'.

154 if (a > b) {
109 if (a > b) {
   ~

ts-check.md:154:9 - error TS2304: Cannot find name 'b'.
ts-check.md:109:9 - error TS2304: Cannot find name 'b'.

154 if (a > b) {
109 if (a > b) {
   ~

ts-check.md:157:28 - error TS2304: Cannot find name 'a'.
ts-check.md:112:28 - error TS2304: Cannot find name 'a'.

157 console.log(\`not true: \${a} < \${b}\`)
112 console.log(\`not true: \${a} < \${b}\`)
   ~

ts-check.md:157:35 - error TS2304: Cannot find name 'b'.
ts-check.md:112:35 - error TS2304: Cannot find name 'b'.

157 console.log(\`not true: \${a} < \${b}\`)
112 console.log(\`not true: \${a} < \${b}\`)
   ~

ts-check.md:160:8 - error TS2339: Property 'AwesomeAPI' does not exist on type 'Window & typeof globalThis'.
ts-check.md:115:8 - error TS2339: Property 'AwesomeAPI' does not exist on type 'Window & typeof globalThis'.

160 window.AwesomeAPI.bar('baz')
115 window.AwesomeAPI.bar('baz')
   ~~~~~~~~~~

ts-check.md:174:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.
ts-check.md:129:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.

174 BrowserWindow.wrongAPI('foo')
129 BrowserWindow.wrongAPI('foo')
   ~~~~~~~~

ts-check.md:180:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.
ts-check.md:135:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.

180 BrowserWindow.wrongAPI('foo')
135 BrowserWindow.wrongAPI('foo')
   ~~~~~~~~

ts-check.md:212:8 - error TS2339: Property 'AwesomeAPI' does not exist on type 'Window & typeof globalThis'.
ts-check.md:167:8 - error TS2339: Property 'AwesomeAPI' does not exist on type 'Window & typeof globalThis'.

212 window.AwesomeAPI.foo(42)
167 window.AwesomeAPI.foo(42)
   ~~~~~~~~~~

ts-check.md:4:9 - error TS2339: Property 'foo' does not exist on type 'Console'.

4 console.foo('whoops')
   ~~~

ts-check.md:66:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.
ts-check.md:54:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.

66 BrowserWindow.wrongAPI('foo')
54 BrowserWindow.wrongAPI('foo')
   ~~~~~~~~

ts-check.md:72:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.
ts-check.md:60:15 - error TS2339: Property 'wrongAPI' does not exist on type 'typeof BrowserWindow'.

72 BrowserWindow.wrongAPI('foo')
60 BrowserWindow.wrongAPI('foo')
   ~~~~~~~~

ts-check.md:8:9 - error TS2339: Property 'foo' does not exist on type 'Console'.

8 console.foo('whoops')
   ~~~

ts-check.md:95:8 - error TS2339: Property 'myAwesomeAPI' does not exist on type 'Window & typeof globalThis'.
ts-check.md:83:8 - error TS2339: Property 'myAwesomeAPI' does not exist on type 'Window & typeof globalThis'.

95 window.myAwesomeAPI()
83 window.myAwesomeAPI()
   ~~~~~~~~~~~~


Found 14 errors in 10 files.
Found 13 errors in 9 files.

Errors Files
1 ts-check.md:128
5 ts-check.md:154
1 ts-check.md:174
1 ts-check.md:180
1 ts-check.md:212
5 ts-check.md:109
1 ts-check.md:129
1 ts-check.md:135
1 ts-check.md:167
1 ts-check.md:4
1 ts-check.md:66
1 ts-check.md:72
1 ts-check.md:54
1 ts-check.md:60
1 ts-check.md:8
1 ts-check.md:95
1 ts-check.md:83

"
`;
49 changes: 2 additions & 47 deletions tests/fixtures/ts-check.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,6 @@ console.bar('whoops')

These blocks suppress specific lines (1-based)

```js @ts-ignore=[3]
console.log('test')

window.myAwesomeAPI()
```

```js title='main.js' @ts-ignore=[3]
console.log('test')

window.myAwesomeAPI()
```

```js @ts-expect-error=[3]
console.log('test')

Expand All @@ -46,13 +34,13 @@ window.myAwesomeAPI()

These blocks have conflicting options

```js @ts-nocheck @ts-ignore=[3]
```js @ts-nocheck @ts-expect-error=[3]
console.log('test')

window.myAwesomeAPI()
```

```js @ts-nocheck title='main.js' @ts-ignore=[3]
```js @ts-nocheck title='main.js' @ts-expect-error=[3]
console.log('test')

window.myAwesomeAPI()
Expand All @@ -72,39 +60,6 @@ const { BrowserWindow } = require('electron')
BrowserWindow.wrongAPI('foo')
```

These blocks have multiple @ts-ignore lines

```js @ts-ignore=[3,5]
console.log('test')

window.myAwesomeAPI()

window.myOtherAwesomeAPI()
```

```js @ts-ignore=[1,4]
window.myAwesomeAPI()

console.log('test')
window.myOtherAwesomeAPI()
```

This confirms @ts-ignore output is stripped

```js @ts-ignore=[2]
window.myAwesomeAPI()
window.myOtherAwesomeAPI()
```

This confirms @ts-ignore works if the previous line is a comment

```js @ts-ignore=[4]
console.log('test')

// This is a comment
window.myAwesomeAPI()
```

These blocks have multiple @ts-expect-error lines

```js @ts-expect-error=[3,5]
Expand Down
7 changes: 6 additions & 1 deletion tests/lint-roller-markdown-ts-check.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,12 @@ function runLintMarkdownTsCheck(...args: string[]) {
return cp.spawnSync(
process.execPath,
[path.resolve(__dirname, '../dist/bin/lint-markdown-ts-check.js'), ...args],
{ stdio: 'pipe', encoding: 'utf-8', cwd: FIXTURES_DIR },
{
stdio: 'pipe',
encoding: 'utf-8',
cwd: FIXTURES_DIR,
env: { NODE_OPTIONS: '--no-deprecation' },
},
);
}

Expand Down