-
Notifications
You must be signed in to change notification settings - Fork 211
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1aa87f4
commit ab6e910
Showing
17 changed files
with
377 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,5 @@ node_modules | |
dist | ||
*.tsbuildinfo | ||
*.lerna_backup | ||
packages/linting/cache | ||
packages/utils/cache |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
logs |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
src | ||
test | ||
logs | ||
.DS_Store | ||
.vscode | ||
*.tsbuildinfo | ||
tsconfig.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
# @definitelytyped/linting | ||
|
||
Linting utilities for DefinitelyTyped. Mostly extracted from [microsoft/dtslint](https://github.com/microsoft/dtslint). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
{ | ||
"name": "@definitelytyped/linting", | ||
"version": "0.0.84", | ||
"description": "Linting utilities for DefinitelyTyped", | ||
"homepage": "https://github.com/microsoft/DefinitelyTyped-tools/tree/master/packages/linting#readme", | ||
"license": "MIT", | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/microsoft/DefinitelyTyped-tools.git", | ||
"directory": "packages/linting" | ||
}, | ||
"scripts": { | ||
"build": "tsc -b" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/microsoft/DefinitelyTyped-tools/issues" | ||
}, | ||
"dependencies": { | ||
"@definitelytyped/header-parser": "^0.0.84", | ||
"@definitelytyped/typescript-versions": "^0.0.84", | ||
"@definitelytyped/utils": "^0.0.84", | ||
"@types/node": "^14.14.35", | ||
"charm": "^1.0.2", | ||
"fs-extra": "^8.1.0", | ||
"fstream": "^1.0.12", | ||
"npm-registry-client": "^8.6.0", | ||
"strip-json-comments": "^4.0.0", | ||
"tar": "^2.2.2", | ||
"tar-stream": "^2.1.4", | ||
"typescript": "^4.1.0" | ||
}, | ||
"devDependencies": { | ||
"@types/charm": "^1.0.1", | ||
"@types/fs-extra": "^8.1.0", | ||
"@types/tar": "^4.0.3", | ||
"@types/tar-stream": "^2.1.0" | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { makeTypesVersionsForPackageJson } from "@definitelytyped/header-parser"; | ||
import { TypeScriptVersion } from "@definitelytyped/typescript-versions"; | ||
import assert = require("assert"); | ||
import { pathExists } from "fs-extra"; | ||
import { join as joinPaths } from "path"; | ||
|
||
import { readJsonWithComments } from "./readJsonWithComments"; | ||
|
||
export async function checkPackageJson( | ||
dirPath: string, | ||
typesVersions: readonly TypeScriptVersion[], | ||
): Promise<void> { | ||
const pkgJsonPath = joinPaths(dirPath, "package.json"); | ||
const needsTypesVersions = typesVersions.length !== 0; | ||
if (!await pathExists(pkgJsonPath)) { | ||
if (needsTypesVersions) { | ||
throw new Error(`${dirPath}: Must have 'package.json' for "typesVersions"`); | ||
} | ||
return; | ||
} | ||
|
||
const pkgJson = await readJsonWithComments(pkgJsonPath) as Record<string, unknown>; | ||
|
||
if ((pkgJson as any).private !== true) { | ||
throw new Error(`${pkgJsonPath} should set \`"private": true\``); | ||
} | ||
|
||
if (needsTypesVersions) { | ||
assert.strictEqual((pkgJson as any).types, "index", `"types" in '${pkgJsonPath}' should be "index".`); | ||
const expected = makeTypesVersionsForPackageJson(typesVersions); | ||
assert.deepEqual((pkgJson as any).typesVersions, expected, | ||
`"typesVersions" in '${pkgJsonPath}' is not set right. Should be: ${JSON.stringify(expected, undefined, 4)}`); | ||
} | ||
|
||
for (const key in pkgJson) { // tslint:disable-line forin | ||
switch (key) { | ||
case "private": | ||
case "dependencies": | ||
case "license": | ||
// "private"/"typesVersions"/"types" checked above, "dependencies" / "license" checked by types-publisher, | ||
break; | ||
case "typesVersions": | ||
case "types": | ||
if (!needsTypesVersions) { | ||
throw new Error(`${pkgJsonPath} doesn't need to set "${key}" when no 'ts3.x' directories exist.`); | ||
} | ||
break; | ||
default: | ||
throw new Error(`${pkgJsonPath} should not include field ${key}`); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
import { getCompilerOptions } from "./util"; | ||
|
||
export interface DefinitelyTypedInfo { | ||
/** "../" or "../../" or "../../../". This should use '/' even on windows. */ | ||
readonly relativeBaseUrl: string; | ||
} | ||
|
||
export async function checkTsconfig(dirPath: string, dt: DefinitelyTypedInfo | undefined): Promise<void> { | ||
const options = await getCompilerOptions(dirPath); | ||
|
||
if (dt) { | ||
const { relativeBaseUrl } = dt; | ||
|
||
const mustHave = { | ||
module: "commonjs", | ||
noEmit: true, | ||
forceConsistentCasingInFileNames: true, | ||
baseUrl: relativeBaseUrl, | ||
typeRoots: [relativeBaseUrl], | ||
types: [], | ||
}; | ||
|
||
for (const key of Object.getOwnPropertyNames(mustHave) as (keyof typeof mustHave)[]) { | ||
const expected = mustHave[key]; | ||
const actual = options[key]; | ||
if (!deepEquals(expected, actual)) { | ||
throw new Error(`Expected compilerOptions[${JSON.stringify(key)}] === ${JSON.stringify(expected)}`); | ||
} | ||
} | ||
|
||
for (const key in options) { // tslint:disable-line forin | ||
switch (key) { | ||
case "lib": | ||
case "noImplicitAny": | ||
case "noImplicitThis": | ||
case "strict": | ||
case "strictNullChecks": | ||
case "noUncheckedIndexedAccess": | ||
case "strictFunctionTypes": | ||
case "esModuleInterop": | ||
case "allowSyntheticDefaultImports": | ||
// Allow any value | ||
break; | ||
case "target": | ||
case "paths": | ||
case "jsx": | ||
case "jsxFactory": | ||
case "experimentalDecorators": | ||
case "noUnusedLocals": | ||
case "noUnusedParameters": | ||
// OK. "paths" checked further by types-publisher | ||
break; | ||
default: | ||
if (!(key in mustHave)) { | ||
throw new Error(`Unexpected compiler option ${key}`); | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (!("lib" in options)) { | ||
throw new Error('Must specify "lib", usually to `"lib": ["es6"]` or `"lib": ["es6", "dom"]`.'); | ||
} | ||
|
||
if ("strict" in options) { | ||
if (options.strict !== true) { | ||
throw new Error('When "strict" is present, it must be set to `true`.'); | ||
} | ||
|
||
for (const key of ["noImplicitAny", "noImplicitThis", "strictNullChecks", "strictFunctionTypes"]) { | ||
if (key in options) { | ||
throw new TypeError(`Expected "${key}" to not be set when "strict" is \`true\`.`); | ||
} | ||
} | ||
} else { | ||
for (const key of ["noImplicitAny", "noImplicitThis", "strictNullChecks", "strictFunctionTypes"]) { | ||
if (!(key in options)) { | ||
throw new Error(`Expected \`"${key}": true\` or \`"${key}": false\`.`); | ||
} | ||
} | ||
} | ||
|
||
if (options.types && options.types.length) { | ||
throw new Error( | ||
'Use `/// <reference types="..." />` directives in source files and ensure ' + | ||
'that the "types" field in your tsconfig is an empty array.'); | ||
} | ||
} | ||
|
||
function deepEquals(expected: unknown, actual: unknown): boolean { | ||
if (expected instanceof Array) { | ||
return actual instanceof Array | ||
&& actual.length === expected.length | ||
&& expected.every((e, i) => deepEquals(e, actual[i])); | ||
} else { | ||
return expected === actual; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { pathExists } from "fs-extra"; | ||
import { join as joinPaths } from "path"; | ||
|
||
import { readJsonWithComments } from "./readJsonWithComments"; | ||
|
||
export async function checkTslintJson(dirPath: string, dt: boolean): Promise<void> { | ||
const configPath = getConfigPath(dirPath); | ||
const shouldExtend = `dtslint/${dt ? "dt" : "dtslint"}.json`; | ||
const validateExtends = (extend: string | string[]) => | ||
extend === shouldExtend || (!dt && Array.isArray(extend) && extend.some(val => val === shouldExtend)); | ||
|
||
if (!await pathExists(configPath)) { | ||
if (dt) { | ||
throw new Error( | ||
`On DefinitelyTyped, must include \`tslint.json\` containing \`{ "extends": "${shouldExtend}" }\`.\n` + | ||
"This was inferred as a DefinitelyTyped package because it contains a `// Type definitions for` header."); | ||
} | ||
return; | ||
} | ||
|
||
const tslintJson = await readJsonWithComments(configPath); | ||
if (!validateExtends(tslintJson.extends)) { | ||
throw new Error(`If 'tslint.json' is present, it should extend "${shouldExtend}"`); | ||
} | ||
} | ||
|
||
function getConfigPath(dirPath: string): string { | ||
return joinPaths(dirPath, "tslint.json"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { existsSync, readFileSync } from "fs"; | ||
import { dirname, resolve as resolvePath } from "path"; | ||
import * as TsType from "typescript"; | ||
|
||
const programCache = new WeakMap<TsType.Program, Map<string, TsType.Program>>(); | ||
|
||
/** Maps a Program to one created with the version specified in `options`. */ | ||
export function getProgram(configFile: string, ts: typeof TsType, versionName: string, lintProgram: TsType.Program): TsType.Program { | ||
let versionToProgram = programCache.get(lintProgram); | ||
if (versionToProgram === undefined) { | ||
versionToProgram = new Map<string, TsType.Program>(); | ||
programCache.set(lintProgram, versionToProgram); | ||
} | ||
|
||
let newProgram = versionToProgram.get(versionName); | ||
if (newProgram === undefined) { | ||
newProgram = createProgram(configFile, ts); | ||
versionToProgram.set(versionName, newProgram); | ||
} | ||
return newProgram; | ||
} | ||
|
||
function createProgram(configFile: string, ts: typeof TsType): TsType.Program { | ||
const projectDirectory = dirname(configFile); | ||
const { config } = ts.readConfigFile(configFile, ts.sys.readFile); | ||
const parseConfigHost: TsType.ParseConfigHost = { | ||
fileExists: existsSync, | ||
readDirectory: ts.sys.readDirectory, | ||
readFile: file => readFileSync(file, "utf8"), | ||
useCaseSensitiveFileNames: true, | ||
}; | ||
const parsed = ts.parseJsonConfigFileContent(config, parseConfigHost, resolvePath(projectDirectory), { noEmit: true }); | ||
const host = ts.createCompilerHost(parsed.options, true); | ||
return ts.createProgram(parsed.fileNames, parsed.options, host); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * from "./checkPackageJson"; | ||
export * from "./checkTsconfig"; | ||
export * from "./checkTslintJson"; | ||
export * from "./testDependencies"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { readFile } from "fs-extra"; | ||
import stripJsonComments from "strip-json-comments"; | ||
|
||
export async function readJsonWithComments(path: string) { | ||
const text = await readFile(path, "utf-8"); | ||
return JSON.parse(stripJsonComments(text)); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import { TypeScriptVersion } from "@definitelytyped/typescript-versions"; | ||
import { typeScriptPath } from "@definitelytyped/utils"; | ||
import assert = require("assert"); | ||
import { dirname, join as joinPaths, normalize } from "path"; | ||
import * as TsType from "typescript"; | ||
|
||
import { getProgram } from "./getProgram"; | ||
|
||
export type TsVersion = TypeScriptVersion | "local"; | ||
|
||
export function testDependencies( | ||
version: TsVersion, | ||
dirPath: string, | ||
lintProgram: TsType.Program, | ||
tsLocal: string | undefined, | ||
): string | undefined { | ||
const tsconfigPath = joinPaths(dirPath, "tsconfig.json"); | ||
assert(version !== "local" || tsLocal); | ||
const ts: typeof TsType = require(typeScriptPath(version, tsLocal)); | ||
const program = getProgram(tsconfigPath, ts, version, lintProgram); | ||
const diagnostics = ts.getPreEmitDiagnostics(program).filter(d => !d.file || isExternalDependency(d.file, dirPath, program)); | ||
if (!diagnostics.length) { return undefined; } | ||
|
||
const showDiags = ts.formatDiagnostics(diagnostics, { | ||
getCanonicalFileName: f => f, | ||
getCurrentDirectory: () => dirPath, | ||
getNewLine: () => "\n", | ||
}); | ||
|
||
const message = `Errors in typescript@${version} for external dependencies:\n${showDiags}`; | ||
|
||
// Add an edge-case for someone needing to `npm install` in react when they first edit a DT module which depends on it - #226 | ||
const cannotFindDepsDiags = diagnostics.find(d => d.code === 2307 && d.messageText.toString().includes("Cannot find module")); | ||
if (cannotFindDepsDiags && cannotFindDepsDiags.file) { | ||
const path = cannotFindDepsDiags.file.fileName; | ||
const typesFolder = dirname(path); | ||
|
||
return ` | ||
A module look-up failed, this often occurs when you need to run \`npm install\` on a dependent module before you can lint. | ||
Before you debug, first try running: | ||
npm install --prefix ${typesFolder} | ||
Then re-run. Full error logs are below. | ||
${message}`; | ||
} else { | ||
return message; | ||
} | ||
} | ||
|
||
function isExternalDependency(file: TsType.SourceFile, dirPath: string, program: TsType.Program): boolean { | ||
return !startsWithDirectory(file.fileName, dirPath) || program.isSourceFileFromExternalLibrary(file); | ||
} | ||
|
||
function normalizePath(file: string) { | ||
// replaces '\' with '/' and forces all DOS drive letters to be upper-case | ||
return normalize(file) | ||
.replace(/\\/g, "/") | ||
.replace(/^[a-z](?=:)/, c => c.toUpperCase()); | ||
} | ||
|
||
function startsWithDirectory(filePath: string, dirPath: string): boolean { | ||
const normalFilePath = normalizePath(filePath); | ||
const normalDirPath = normalizePath(dirPath).replace(/\/$/, ""); | ||
return normalFilePath.startsWith(normalDirPath + "/") || normalFilePath.startsWith(normalDirPath + "\\"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { pathExists } from "fs-extra"; | ||
import { join } from "path"; | ||
import * as ts from "typescript"; | ||
|
||
import { readJsonWithComments } from "./readJsonWithComments"; | ||
|
||
export async function getCompilerOptions(dirPath: string): Promise<ts.CompilerOptions> { | ||
const tsconfigPath = join(dirPath, "tsconfig.json"); | ||
if (!await pathExists(tsconfigPath)) { | ||
throw new Error(`Need a 'tsconfig.json' file in ${dirPath}`); | ||
} | ||
return (await readJsonWithComments(tsconfigPath)).compilerOptions; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"extends": "../../tsconfig.base.json", | ||
"compilerOptions": { | ||
"rootDir": "src", | ||
"outDir": "dist" | ||
}, | ||
"include": ["src"], | ||
"references": [{ "path": "../header-parser" }, { "path": "../typescript-versions" }, { "path": "../utils" }] | ||
} |
Oops, something went wrong.