Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1.0 for Twoslash #965

Merged
merged 3 commits into from
Aug 25, 2020
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
12 changes: 12 additions & 0 deletions .github/workflows/builds-on-windows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ jobs:
with:
node-version: "13.x"

# Cache yarn deps, to speed up CI
- name: Get yarn cache directory path
id: yarn-cache-dir-path
run: echo "::set-output name=dir::$(yarn config get cacheFolder)"

- uses: actions/cache@v1
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
${{ runner.os }}-yarn-

# Get local dependencies
- run: yarn install
env:
Expand Down
4 changes: 2 additions & 2 deletions packages/gatsby-remark-shiki-twoslash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
"lint": "tsdx lint"
},
"dependencies": {
"@typescript/twoslash": "0.6.2",
"@typescript/vfs": "1.1.2",
"@typescript/twoslash": "1.0.0",
"@typescript/vfs": "1.2.0",
"shiki": "^0.1.6",
"shiki-languages": "^0.1.6",
"shiki-twoslash": "0.7.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/shiki-twoslash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"lint": "tsdx lint"
},
"dependencies": {
"@typescript/twoslash": "0.6.2",
"@typescript/vfs": "1.1.2",
"@typescript/twoslash": "1.0.0",
"@typescript/vfs": "1.2.0",
"shiki": "^0.1.6",
"shiki-languages": "^0.1.6",
"typescript": "*"
Expand Down
5 changes: 5 additions & 0 deletions packages/ts-twoslasher/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 1.0.0

- Supports falling back to _your project's_ node modules for resolving types and imports. This drastically simplifies setting up a code sample which relies on types not shipped with TypeScript.
- Support for adding vfs JSON files in a code sample

## 0.5.0

- Support TS 4.0
Expand Down
4 changes: 2 additions & 2 deletions packages/ts-twoslasher/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@typescript/twoslash",
"version": "0.6.2",
"version": "1.0.0",
"license": "MIT",
"author": "TypeScript team",
"main": "dist/index.js",
Expand Down Expand Up @@ -39,7 +39,7 @@
"lz-string": false
},
"dependencies": {
"@typescript/vfs": "1.1.2",
"@typescript/vfs": "1.2.0",
"debug": "^4.1.1",
"lz-string": "^1.4.4"
}
Expand Down
55 changes: 37 additions & 18 deletions packages/ts-twoslasher/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
let hasLocalStorage = false
try {
hasLocalStorage = typeof localStorage !== `undefined`
} catch (error) { }
} catch (error) {}
const hasProcess = typeof process !== `undefined`
const shouldDebug = (hasLocalStorage && localStorage.getItem("DEBUG")) || (hasProcess && process.env.DEBUG)

Expand All @@ -21,7 +21,7 @@ import {
} from "./utils"
import { validateInput, validateCodeForErrors } from "./validation"

import { createSystem, createVirtualTypeScriptEnvironment, createDefaultMapFromNodeModules } from "@typescript/vfs"
import { createSystem, createVirtualTypeScriptEnvironment, createFSBackedSystem } from "@typescript/vfs"

const log = shouldDebug ? console.log : (_message?: any, ..._optionalParams: any[]) => ""

Expand Down Expand Up @@ -171,7 +171,7 @@ const valuedConfigRegexp = /^\/\/\s?@(\w+):\s?(.+)$/

function filterCompilerOptions(codeLines: string[], defaultCompilerOptions: CompilerOptions, ts: TS) {
const options = { ...defaultCompilerOptions }
for (let i = 0; i < codeLines.length;) {
for (let i = 0; i < codeLines.length; ) {
let match
if ((match = booleanConfigRegexp.exec(codeLines[i]))) {
options[match[1]] = true
Expand Down Expand Up @@ -383,8 +383,16 @@ export function twoslasher(code: string, extension: string, options: TwoSlashOpt
const handbookOptions = { ...filterHandbookOptions(codeLines), ...options.defaultOptions }
const compilerOptions = filterCompilerOptions(codeLines, defaultCompilerOptions, ts)

const vfs = options.fsMap ?? createLocallyPoweredVFS(compilerOptions, ts)
const system = createSystem(vfs)
const getRoot = () => {
const path = require("path")
return process.cwd().split(path.sep).join(path.posix.sep)
}
// In a browser we want to DI everything, in node we can use local infra
const useFS = !!options.fsMap
const vfs = useFS && options.fsMap ? options.fsMap : new Map<string, string>()
const system = useFS ? createSystem(vfs) : createFSBackedSystem(vfs, getRoot(), ts)
const fsRoot = useFS ? "/" : getRoot() + "/"

const env = createVirtualTypeScriptEnvironment(system, [], ts, compilerOptions, options.customTransformers)
const ls = env.languageService

Expand All @@ -394,13 +402,20 @@ export function twoslasher(code: string, extension: string, options: TwoSlashOpt
let queries = [] as TwoSlashReturn["queries"]
let highlights = [] as TwoSlashReturn["highlights"]

const nameContent = splitTwoslashCodeInfoFiles(code, defaultFileName)
const nameContent = splitTwoslashCodeInfoFiles(code, defaultFileName, fsRoot)
const sourceFiles = ["js", "jsx", "ts", "tsx"]

/** All of the referenced files in the markup */
const filenames = nameContent.map(nc => nc[0])

for (const file of nameContent) {
const [filename, codeLines] = file
const filetype = filename.split(".").pop() || ""

// Only run the LSP-y things on source files
if (!sourceFiles.includes(filetype)) {
continue
}

// Create the file in the vfs
const newFileCode = codeLines.join("\n")
Expand Down Expand Up @@ -492,6 +507,13 @@ export function twoslasher(code: string, extension: string, options: TwoSlashOpt
// const declaredFiles = Object.keys(fileMap)

filenames.forEach(file => {
const filetype = file.split(".").pop() || ""

// Only run the LSP-y things on source files
if (!sourceFiles.includes(filetype)) {
return
}

if (!handbookOptions.noErrors) {
errs.push(...ls.getSemanticDiagnostics(file))
errs.push(...ls.getSyntacticDiagnostics(file))
Expand Down Expand Up @@ -598,21 +620,21 @@ export function twoslasher(code: string, extension: string, options: TwoSlashOpt
if (handbookOptions.showEmit) {
// Get the file which created the file we want to show:
const emitFilename = handbookOptions.showEmittedFile || defaultFileName
const emitSourceFilename = emitFilename.replace(".js", "").replace(".d.ts", "").replace(".map", "")
const emitSourceFilename = fsRoot + emitFilename.replace(".js", "").replace(".d.ts", "").replace(".map", "")
const emitSource = filenames.find(f => f === emitSourceFilename + ".ts" || f === emitSourceFilename + ".tsx")

if (!emitSource) {
const allFiles = filenames.join(", ")
throw new Error(
`Cannot find the corresponding source file for ${emitFilename} ${handbookOptions.showEmittedFile} - in ${allFiles}`
)
// prettier-ignore
throw new Error(`Cannot find the corresponding source file for ${emitFilename} (looking for: ${emitSourceFilename} in the vfs) - in ${allFiles}`)
}

const output = ls.getEmitOutput(emitSource)
const file = output.outputFiles.find(o => o.name === handbookOptions.showEmittedFile)
const file = output.outputFiles.find(o => o.name === fsRoot + handbookOptions.showEmittedFile)
if (!file) {
const allFiles = output.outputFiles.map(o => o.name).join(", ")
throw new Error(`Cannot find the file ${handbookOptions.showEmittedFile} - in ${allFiles}`)
// prettier-ignore
throw new Error(`Cannot find the file ${handbookOptions.showEmittedFile} (looking for: ${fsRoot + handbookOptions.showEmittedFile} in the vfs) - in ${allFiles}`)
}

code = file.text
Expand Down Expand Up @@ -676,10 +698,7 @@ export function twoslasher(code: string, extension: string, options: TwoSlashOpt
}
}

const createLocallyPoweredVFS = (compilerOptions: CompilerOptions, ts?: typeof import("typescript")) =>
createDefaultMapFromNodeModules(compilerOptions, ts)

const splitTwoslashCodeInfoFiles = (code: string, defaultFileName: string) => {
const splitTwoslashCodeInfoFiles = (code: string, defaultFileName: string, root: string) => {
const lines = code.split(/\r\n?|\n/g)

let nameForFile = code.includes(`@filename: ${defaultFileName}`) ? "global.ts" : defaultFileName
Expand All @@ -688,14 +707,14 @@ const splitTwoslashCodeInfoFiles = (code: string, defaultFileName: string) => {

for (const line of lines) {
if (line.includes("// @filename: ")) {
fileMap.push([nameForFile, currentFileContent])
fileMap.push([root + nameForFile, currentFileContent])
nameForFile = line.split("// @filename: ")[1].trim()
currentFileContent = []
} else {
currentFileContent.push(line)
}
}
fileMap.push([nameForFile, currentFileContent])
fileMap.push([root + nameForFile, currentFileContent])

// Basically, strip these:
// ["index.ts", []]
Expand Down
64 changes: 32 additions & 32 deletions packages/ts-twoslasher/test/fixtures.test.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
import { readdirSync, readFileSync, lstatSync } from 'fs'
import { join, extname, parse } from 'path'
import { toMatchFile } from 'jest-file-snapshot'
import { twoslasher } from '../src/index'
import { format } from 'prettier'
import { readdirSync, readFileSync, lstatSync } from "fs"
import { join, extname, parse } from "path"
import { toMatchFile } from "jest-file-snapshot"
import { twoslasher } from "../src/index"
import { format } from "prettier"

expect.extend({ toMatchFile })

// To add a test, create a file in the fixtures folder and it will will run through
// as though it was the codeblock.

describe('with fixtures', () => {
describe("with fixtures", () => {
// Add all codefixes
const fixturesFolder = join(__dirname, 'fixtures')
const resultsFolder = join(__dirname, 'results')
const fixturesFolder = join(__dirname, "fixtures")
const resultsFolder = join(__dirname, "results")

readdirSync(join(fixturesFolder, 'tests')).forEach(fixtureName => {
const fixture = join(fixturesFolder, 'tests', fixtureName)
readdirSync(join(fixturesFolder, "tests")).forEach(fixtureName => {
const fixture = join(fixturesFolder, "tests", fixtureName)
if (lstatSync(fixture).isDirectory()) {
return
}

// if(!fixtureName.includes("compiler_fl")) return
it('Hidden Fixtures: ' + fixtureName, () => {
const resultName = parse(fixtureName).name + '.json'
const result = join(resultsFolder, 'tests', resultName)
// if(!fixtureName.includes("imports")) return
it("Hidden Fixtures: " + fixtureName, () => {
const resultName = parse(fixtureName).name + ".json"
const result = join(resultsFolder, "tests", resultName)

const file = readFileSync(fixture, 'utf8')
const file = readFileSync(fixture, "utf8")

const fourslashed = twoslasher(file, extname(fixtureName).substr(1))
const jsonString = format(JSON.stringify(fourslashed), { parser: 'json' })
const jsonString = format(JSON.stringify(fourslashed), { parser: "json" })
expect(jsonString).toMatchFile(result)
})
})
Expand All @@ -40,50 +40,50 @@ describe('with fixtures', () => {
}

// if(!fixtureName.includes("compiler_fl")) return
it('README Fixtures: ' + fixtureName, () => {
const resultName = parse(fixtureName).name + '.json'
it("README Fixtures: " + fixtureName, () => {
const resultName = parse(fixtureName).name + ".json"
const result = join(resultsFolder, resultName)

const file = readFileSync(fixture, 'utf8')
const file = readFileSync(fixture, "utf8")

const fourslashed = twoslasher(file, extname(fixtureName).substr(1))
const jsonString = format(JSON.stringify(fourslashed), { parser: 'json' })
const jsonString = format(JSON.stringify(fourslashed), { parser: "json" })
expect(jsonString).toMatchFile(result)
})
})

readdirSync(join(fixturesFolder, 'tests')).forEach(fixtureName => {
const fixture = join(fixturesFolder, 'tests', fixtureName)
readdirSync(join(fixturesFolder, "tests")).forEach(fixtureName => {
const fixture = join(fixturesFolder, "tests", fixtureName)
if (lstatSync(fixture).isDirectory()) {
return
}

// if(!fixtureName.includes("compiler_fl")) return
it('Hidden Fixtures: ' + fixtureName, () => {
const resultName = parse(fixtureName).name + '.json'
const result = join(resultsFolder, 'tests', resultName)
it("Hidden Fixtures: " + fixtureName, () => {
const resultName = parse(fixtureName).name + ".json"
const result = join(resultsFolder, "tests", resultName)

const file = readFileSync(fixture, 'utf8')
const file = readFileSync(fixture, "utf8")

const fourslashed = twoslasher(file, extname(fixtureName).substr(1))
const jsonString = format(JSON.stringify(fourslashed), { parser: 'json' })
const jsonString = format(JSON.stringify(fourslashed), { parser: "json" })
expect(jsonString).toMatchFile(result)
})
})

const throwingFixturesFolder = join(__dirname, 'fixtures', 'throws')
const throwingFixturesFolder = join(__dirname, "fixtures", "throws")

readdirSync(throwingFixturesFolder).forEach(fixtureName => {
const fixture = join(throwingFixturesFolder, fixtureName)
if (lstatSync(fixture).isDirectory()) {
return
}

it('Throwing Fixture: ' + fixtureName, () => {
const resultName = parse(fixtureName).name + '.json'
it("Throwing Fixture: " + fixtureName, () => {
const resultName = parse(fixtureName).name + ".json"
const result = join(resultsFolder, resultName)

const file = readFileSync(fixture, 'utf8')
const file = readFileSync(fixture, "utf8")

let thrown = false
try {
Expand All @@ -93,7 +93,7 @@ describe('with fixtures', () => {
expect(err.message).toMatchFile(result)
}

if (!thrown) throw new Error('Did not throw')
if (!thrown) throw new Error("Did not throw")
})
})
})
14 changes: 14 additions & 0 deletions packages/ts-twoslasher/test/fixtures/importsModules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// @filename: Component.tsx
import React from "react"

export function Hello() {
return (
<div>
<h1>Hello World</h1>
</div>
)
}

// @filename: index.ts
import { Hello } from "./Component"
console.log(Hello)
6 changes: 6 additions & 0 deletions packages/ts-twoslasher/test/fixtures/tests/handlesJSON.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// @moduleResolution: node
// @filename: package.json
{ "name": "thing" }
// @filename: index.ts
// ---cut---
const i = 123
Loading