Skip to content

Commit

Permalink
feat: provide removeTwoslashNotations fallback function
Browse files Browse the repository at this point in the history
  • Loading branch information
antfu committed Feb 9, 2024
1 parent ca7d604 commit 762491e
Show file tree
Hide file tree
Showing 9 changed files with 219 additions and 4 deletions.
1 change: 1 addition & 0 deletions packages/twoslash/build.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export default defineBuildConfig({
entries: [
'src/index',
'src/core',
'src/fallback',
],
declaration: true,
clean: true,
Expand Down
11 changes: 11 additions & 0 deletions packages/twoslash/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@
"./core": {
"import": "./dist/core.mjs",
"require": "./dist/core.cjs"
},
"./fallback": {
"import": "./dist/fallback.mjs",
"require": "./dist/fallback.cjs"
}
},
"main": "dist/index.mjs",
"module": "dist/index.mjs",
"types": "dist/index.d.mts",
"typesVersions": {
"*": {
"./core": ["./dist/core.d.mts"],
"./fallback": ["./dist/fallback.d.mts"],
"*": ["./dist/index.d.mts"]
}
},
"files": [
"dist"
],
Expand Down
17 changes: 17 additions & 0 deletions packages/twoslash/scripts/flag-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import fs from 'node:fs/promises'
import ts from 'typescript'
import type { CompilerOptionDeclaration } from '../src/types/options'
import { defaultHandbookOptions } from '../src/defaults'

async function generateFlagKeys() {
const tsOptionDeclarations = (ts as any).optionDeclarations as CompilerOptionDeclaration[]

const keys = [
...tsOptionDeclarations.map(i => i.name),
...Object.keys(defaultHandbookOptions),
].sort()

await fs.writeFile('src/flag-keys.ts', `// Generated by scripts/flag-keys.ts\nexport const flagKeys = ${JSON.stringify(keys, null, 2)}`, 'utf-8')
}

generateFlagKeys()
2 changes: 1 addition & 1 deletion packages/twoslash/src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ export function createTwoslasher(createOptions: CreateTwoslashOptions = {}): Two
const pc = createPositionConverter(code)

// extract cuts
meta.removals.push(...findCutNotations(code))
findCutNotations(code, meta)
// extract markers
findQueryMarkers(code, meta, pc)

Expand Down
40 changes: 40 additions & 0 deletions packages/twoslash/src/fallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { removeCodeRanges } from 'twoslash-protocol'
import { reAnnonateMarkers, reConfigBoolean, reConfigValue } from './regexp'
import type { TwoslashReturnMeta } from './types'
import { findCutNotations } from './utils'
import { flagKeys } from './flag-keys'

/**
* A fallback function to strip out twoslash annotations from a string and does nothing else.
*
* This function does not returns the meta information about the removals.
* It's designed to be used as a fallback when Twoslash fails.
*/
export function removeTwoslashNotations(code: string, customTags?: string[]): string {
const meta: Pick<TwoslashReturnMeta, 'removals'> = {
removals: [],
}
const tags = [
...customTags ?? [],
...flagKeys,
]

Array.from(code.matchAll(reConfigBoolean)).forEach((match) => {
if (!tags.includes(match[1]))
return
meta.removals.push([match.index!, match.index! + match[0].length + 1])
})
Array.from(code.matchAll(reConfigValue)).forEach((match) => {
if (!tags.includes(match[1]))
return
meta.removals.push([match.index!, match.index! + match[0].length + 1])
})

findCutNotations(code, meta)
Array.from(code.matchAll(reAnnonateMarkers)).forEach((match) => {
const index = match.index!
meta.removals.push([index, index + match[0].length + 1])
})

return removeCodeRanges(code, meta.removals).code
}
132 changes: 132 additions & 0 deletions packages/twoslash/src/flag-keys.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Generated by scripts/flag-keys.ts
export const flagKeys = [
'all',
'allowArbitraryExtensions',
'allowImportingTsExtensions',
'allowJs',
'allowSyntheticDefaultImports',
'allowUmdGlobalAccess',
'allowUnreachableCode',
'allowUnusedLabels',
'alwaysStrict',
'assumeChangesOnlyAffectDirectDependencies',
'baseUrl',
'build',
'charset',
'checkJs',
'composite',
'customConditions',
'declaration',
'declarationDir',
'declarationMap',
'diagnostics',
'disableReferencedProjectLoad',
'disableSizeLimit',
'disableSolutionSearching',
'disableSourceOfProjectReferenceRedirect',
'downlevelIteration',
'emitBOM',
'emitDeclarationOnly',
'emitDecoratorMetadata',
'errors',
'esModuleInterop',
'exactOptionalPropertyTypes',
'experimentalDecorators',
'explainFiles',
'extendedDiagnostics',
'forceConsistentCasingInFileNames',
'generateCpuProfile',
'generateTrace',
'help',
'help',
'ignoreDeprecations',
'importHelpers',
'importsNotUsedAsValues',
'incremental',
'init',
'inlineSourceMap',
'inlineSources',
'isolatedModules',
'jsx',
'jsxFactory',
'jsxFragmentFactory',
'jsxImportSource',
'keepNotations',
'keyofStringsOnly',
'lib',
'listEmittedFiles',
'listFiles',
'listFilesOnly',
'locale',
'mapRoot',
'maxNodeModuleJsDepth',
'module',
'moduleDetection',
'moduleResolution',
'moduleSuffixes',
'newLine',
'noEmit',
'noEmitHelpers',
'noEmitOnError',
'noErrorTruncation',
'noErrorValidation',
'noErrors',
'noErrorsCutted',
'noFallthroughCasesInSwitch',
'noImplicitAny',
'noImplicitOverride',
'noImplicitReturns',
'noImplicitThis',
'noImplicitUseStrict',
'noLib',
'noPropertyAccessFromIndexSignature',
'noResolve',
'noStaticSemanticInfo',
'noStrictGenericChecks',
'noUncheckedIndexedAccess',
'noUnusedLocals',
'noUnusedParameters',
'out',
'outDir',
'outFile',
'paths',
'plugins',
'preserveConstEnums',
'preserveSymlinks',
'preserveValueImports',
'preserveWatchOutput',
'pretty',
'project',
'reactNamespace',
'removeComments',
'resolveJsonModule',
'resolvePackageJsonExports',
'resolvePackageJsonImports',
'rootDir',
'rootDirs',
'showConfig',
'showEmit',
'showEmittedFile',
'skipDefaultLibCheck',
'skipLibCheck',
'sourceMap',
'sourceRoot',
'strict',
'strictBindCallApply',
'strictFunctionTypes',
'strictNullChecks',
'strictPropertyInitialization',
'stripInternal',
'suppressExcessPropertyErrors',
'suppressImplicitAnyIndexErrors',
'target',
'traceResolution',
'tsBuildInfoFile',
'typeRoots',
'types',
'useDefineForClassFields',
'useUnknownInCatchVariables',
'verbatimModuleSyntax',
'version',
'watch',
]
4 changes: 4 additions & 0 deletions packages/twoslash/src/public.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ export {
getObjectHash,
} from './utils'

export {
removeTwoslashNotations,
} from './fallback'

export {
validateCodeForErrors,
} from './validation'
6 changes: 5 additions & 1 deletion packages/twoslash/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ export function findFlagNotations(code: string, customTags: string[], tsOptionDe
return flagNotations
}

export function findCutNotations(code: string) {
export function findCutNotations(code: string, meta: Pick<TwoslashReturnMeta, 'removals'>) {
const removals: Range[] = []

const cutBefore = [...code.matchAll(reCutBefore)]
Expand Down Expand Up @@ -288,6 +288,10 @@ export function findCutNotations(code: string) {
}
removals.push([start.index!, end.index! + end[0].length + 1])
}

if (meta)
meta.removals.push(...removals)

return removals
}

Expand Down
10 changes: 8 additions & 2 deletions packages/twoslash/test/fixtures.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { extname } from 'node:path'
import process from 'node:process'
import { expect, it } from 'vitest'
import type { TwoslashReturn } from '../src/types'
import { createTwoslasher } from '../src/index'
import { createTwoslasher, removeTwoslashNotations } from '../src/index'

// To add a test, create a file in the fixtures folder and it will will run through
// as though it was the codeblock.
Expand Down Expand Up @@ -32,9 +32,10 @@ Object.entries(fixtures).forEach(([path, fixture]) => {
path,
async () => {
let result: TwoslashReturn = undefined!
const code = await fixture()
try {
result = twoslasher(
await fixture(),
code,
inExt,
{
customTags: ['annotate'],
Expand All @@ -58,6 +59,11 @@ Object.entries(fixtures).forEach(([path, fixture]) => {
else {
expect(cleanFixture(result))
.toMatchFileSnapshot(outPath)

if (!result.meta.handbookOptions.showEmit) {
expect(removeTwoslashNotations(code))
.toEqual(result.code)
}
}
},
)
Expand Down

0 comments on commit 762491e

Please sign in to comment.