Skip to content

Commit

Permalink
fix(website): load typescript libs in playground #4493
Browse files Browse the repository at this point in the history
  • Loading branch information
armano2 committed Mar 30, 2022
1 parent 8c98d16 commit 81b244d
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 91 deletions.
50 changes: 45 additions & 5 deletions packages/website-eslint/src/linter/CompilerHost.js
@@ -1,9 +1,39 @@
import { getDefaultLibFileName } from 'typescript';
import { ScriptKind } from 'typescript';

function getScriptKind(isJsx, filePath) {
const extension = (/(\.[a-z]+)$/.exec(filePath)?.[0] || '').toLowerCase();

switch (extension) {
case '.ts':
return ScriptKind.TS;
case '.tsx':
return ScriptKind.TSX;
case '.js':
return ScriptKind.JS;

case '.jsx':
return ScriptKind.JSX;

case '.json':
return ScriptKind.JSON;

default:
// unknown extension, force typescript to ignore the file extension, and respect the user's setting
return isJsx ? ts.ScriptKind.TSX : ts.ScriptKind.TS;
}
}

export class CompilerHost {
constructor(files, sourceFiles) {
this.files = files;
this.sourceFiles = sourceFiles;
constructor(libs, isJsx) {
this.files = [];
this.isJsx = isJsx || false;

if (libs) {
for (const [key, value] of libs) {
this.files[key] = value;
}
}
}

fileExists(name) {
Expand Down Expand Up @@ -39,10 +69,20 @@ export class CompilerHost {
}

readFile(name) {
return this.files[name];
if (this.fileExists(name)) {
return this.files[name];
} else {
return ''; // fallback, in case if file is not available
}
}

getSourceFile(name) {
return this.sourceFiles[name];
return ts.createSourceFile(
name,
this.readFile(name),
ts.ScriptTarget.Latest,
/* setParentNodes */ true,
getScriptKind(this.isJsx, name),
);
}
}
44 changes: 0 additions & 44 deletions packages/website-eslint/src/linter/create-ast-program.js

This file was deleted.

11 changes: 8 additions & 3 deletions packages/website-eslint/src/linter/linter.js
Expand Up @@ -5,15 +5,20 @@ import rules from '@typescript-eslint/eslint-plugin/dist/rules';

const PARSER_NAME = '@typescript-eslint/parser';

export function loadLinter() {
export function loadLinter(libs, compilerOptions) {
const linter = new Linter();
let storedAST;
let storedTsAST;
let storedScope;

linter.defineParser(PARSER_NAME, {
parseForESLint(code, options) {
const toParse = parseForESLint(code, options);
parseForESLint(code, eslintOptions) {
const toParse = parseForESLint(
code,
eslintOptions,
compilerOptions,
libs,
);
storedAST = toParse.ast;
storedTsAST = toParse.tsAst;
storedScope = toParse.scopeManager;
Expand Down
65 changes: 39 additions & 26 deletions packages/website-eslint/src/linter/parser.js
@@ -1,42 +1,55 @@
import { analyze } from '@typescript-eslint/scope-manager/dist/analyze';
import { visitorKeys } from '@typescript-eslint/visitor-keys/dist/visitor-keys';
import { astConverter } from '@typescript-eslint/typescript-estree/dist/ast-converter';
import { createASTProgram } from './create-ast-program.js';
import { extra } from './config.js';
import { CompilerHost } from './CompilerHost';
import { createProgram } from 'typescript';

function parseAndGenerateServices(code, options) {
const { ast, program } = createASTProgram(code, options);
const { estree, astMaps } = astConverter(
ast,
{ ...extra, code, jsx: options.jsx ?? false },
true,
);
export function createASTProgram(code, isJsx, compilerOptions, libs) {
const fileName = isJsx ? '/demo.tsx' : '/demo.ts';
const compilerHost = new CompilerHost(libs, isJsx);

compilerHost.files[fileName] = code;
const program = createProgram(
Object.keys(compilerHost.files),
compilerOptions,
compilerHost,
);
const ast = program.getSourceFile(fileName);
return {
ast: estree,
tsAst: ast,
services: {
hasFullTypeInformation: true,
program,
esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap,
tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap,
},
ast,
program,
};
}

export function parseForESLint(code, parserOptions) {
const { ast, tsAst, services } = parseAndGenerateServices(code, {
...parserOptions,
jsx: parserOptions.ecmaFeatures?.jsx ?? false,
useJSXTextNode: true,
projectFolderIgnoreList: [],
});
export function parseForESLint(code, eslintOptions, compilerOptions, libs) {
const isJsx = eslintOptions.ecmaFeatures?.jsx ?? false;

const { ast: tsAst, program } = createASTProgram(
code,
isJsx,
compilerOptions,
libs,
);

const { estree: ast, astMaps } = astConverter(
tsAst,
{ ...extra, code, jsx: isJsx },
true,
);

const services = {
hasFullTypeInformation: true,
program,
esTreeNodeToTSNodeMap: astMaps.esTreeNodeToTSNodeMap,
tsNodeToESTreeNodeMap: astMaps.tsNodeToESTreeNodeMap,
};

const scopeManager = analyze(ast, {
ecmaVersion:
parserOptions.ecmaVersion === 'latest' ? 1e8 : parserOptions.ecmaVersion,
globalReturn: parserOptions.ecmaFeatures?.globalReturn ?? false,
sourceType: parserOptions.sourceType ?? 'script',
eslintOptions.ecmaVersion === 'latest' ? 1e8 : eslintOptions.ecmaVersion,
globalReturn: eslintOptions.ecmaFeatures?.globalReturn ?? false,
sourceType: eslintOptions.sourceType ?? 'script',
});

return {
Expand Down
7 changes: 5 additions & 2 deletions packages/website-eslint/types/index.d.ts
@@ -1,6 +1,6 @@
import type { TSESLint, TSESTree } from '@typescript-eslint/utils';
import type { ParserOptions } from '@typescript-eslint/types';
import type { SourceFile } from 'typescript';
import type { SourceFile, CompilerOptions } from 'typescript';

export type LintMessage = TSESLint.Linter.LintMessage;
export type RuleFix = TSESLint.RuleFix;
Expand All @@ -22,7 +22,10 @@ export interface WebLinter {
}

export interface LinterLoader {
loadLinter(): WebLinter;
loadLinter(
libMap: Map<string, string>,
compilerOptions: CompilerOptions,
): WebLinter;
}

export type {
Expand Down
30 changes: 19 additions & 11 deletions packages/website/src/components/editor/useSandboxServices.ts
Expand Up @@ -47,7 +47,16 @@ export const useSandboxServices = (
setLoadedTs(props.ts);

sandboxSingleton(props.ts)
.then(({ main, sandboxFactory, ts, linter }) => {
.then(async ({ main, sandboxFactory, ts, linter }) => {
const compilerOptions = {
noResolve: true,
strict: true,
target: main.languages.typescript.ScriptTarget.ESNext,
jsx: props.jsx ? main.languages.typescript.JsxEmit.React : undefined,
lib: ['ESNext'],
module: main.languages.typescript.ModuleKind.ESNext,
};

const sandboxConfig: Partial<SandboxConfig> = {
text: '',
monacoSettings: {
Expand All @@ -57,15 +66,7 @@ export const useSandboxServices = (
scrollBeyondLastLine: false,
smoothScrolling: true,
},
compilerOptions: {
noResolve: true,
strict: true,
target: main.languages.typescript.ScriptTarget.ESNext,
jsx: props.jsx
? main.languages.typescript.JsxEmit.React
: undefined,
module: main.languages.typescript.ModuleKind.ESNext,
},
compilerOptions: compilerOptions,
domID: editorEmbedId,
};

Expand All @@ -75,7 +76,14 @@ export const useSandboxServices = (
ts,
);

const webLinter = linter.loadLinter();
const libMap = await sandboxInstance.tsvfs.createDefaultMapFromCDN(
sandboxInstance.getCompilerOptions(),
props.ts,
true,
window.ts,
);

const webLinter = linter.loadLinter(libMap, compilerOptions);

props.onLoaded(webLinter.ruleNames, sandboxInstance.supportedVersions);

Expand Down

0 comments on commit 81b244d

Please sign in to comment.