2020-12-14 13:13:08 +03:00
"use strict" ;
/ * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Copyright ( c ) Microsoft Corporation . All rights reserved .
* Licensed under the MIT License . See License . txt in the project root for license information .
* -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- * /
Object . defineProperty ( exports , "__esModule" , { value : true } ) ;
const ts = require ( "typescript" ) ;
const fs _1 = require ( "fs" ) ;
const path _1 = require ( "path" ) ;
const minimatch _1 = require ( "minimatch" ) ;
//
// #############################################################################################
//
// A custom typescript checker for the specific task of detecting the use of certain types in a
// layer that does not allow such use. For example:
// - using DOM globals in common/node/electron-main layer (e.g. HTMLElement)
// - using node.js globals in common/browser layer (e.g. process)
//
// Make changes to below RULES to lift certain files from these checks only if absolutely needed
//
// #############################################################################################
//
// Types we assume are present in all implementations of JS VMs (node.js, browsers)
// Feel free to add more core types as you see needed if present in node.js and browsers
const CORE _TYPES = [
'require' ,
'setTimeout' ,
'clearTimeout' ,
'setInterval' ,
'clearInterval' ,
'console' ,
2022-04-04 16:04:29 +03:00
'Console' ,
2020-12-14 13:13:08 +03:00
'Error' ,
2022-04-04 16:04:29 +03:00
'ErrorConstructor' ,
2020-12-14 13:13:08 +03:00
'String' ,
'TextDecoder' ,
'TextEncoder' ,
'self' ,
2022-01-05 13:11:12 +03:00
'queueMicrotask' ,
2022-03-10 18:51:37 +03:00
'Array' ,
'Uint8Array' ,
'Uint16Array' ,
'Uint32Array' ,
'Int8Array' ,
'Int16Array' ,
'Int32Array' ,
'Float32Array' ,
'Float64Array' ,
'Uint8ClampedArray' ,
'BigUint64Array' ,
'BigInt64Array' ,
'btoa' ,
'atob' ,
'AbortSignal' ,
2022-01-05 13:11:12 +03:00
'MessageChannel' ,
2022-04-04 12:21:57 +03:00
'MessagePort' ,
'URL' ,
2022-04-04 16:04:29 +03:00
'URLSearchParams'
2020-12-14 13:13:08 +03:00
] ;
// Types that are defined in a common layer but are known to be only
// available in native environments should not be allowed in browser
const NATIVE _TYPES = [
'NativeParsedArgs' ,
'INativeEnvironmentService' ,
2021-03-15 13:27:31 +03:00
'AbstractNativeEnvironmentService' ,
2020-12-14 13:13:08 +03:00
'INativeWindowConfiguration' ,
'ICommonNativeHostService'
] ;
const RULES = [
// Tests: skip
{
target : '**/vs/**/test/**' ,
skip : true // -> skip all test files
} ,
// Common: vs/base/common/platform.ts
{
target : '**/vs/base/common/platform.ts' ,
allowedTypes : [
... CORE _TYPES ,
// Safe access to postMessage() and friends
'MessageEvent' ,
] ,
disallowedTypes : NATIVE _TYPES ,
disallowedDefinitions : [
'lib.dom.d.ts' ,
'@types/node' // no node.js
]
} ,
2021-03-15 13:27:31 +03:00
// Common: vs/platform/environment/common/*
2020-12-14 13:13:08 +03:00
{
2021-03-15 13:27:31 +03:00
target : '**/vs/platform/environment/common/*.ts' ,
2020-12-14 13:13:08 +03:00
allowedTypes : CORE _TYPES ,
2022-02-08 22:09:00 +03:00
disallowedTypes : [ /* Ignore native types that are defined from here */ ] ,
2020-12-14 13:13:08 +03:00
disallowedDefinitions : [
'lib.dom.d.ts' ,
'@types/node' // no node.js
]
} ,
2022-02-14 10:41:24 +03:00
// Common: vs/platform/window/common/window.ts
2020-12-14 13:13:08 +03:00
{
2022-02-14 10:41:24 +03:00
target : '**/vs/platform/window/common/window.ts' ,
2020-12-14 13:13:08 +03:00
allowedTypes : CORE _TYPES ,
2022-02-08 22:09:00 +03:00
disallowedTypes : [ /* Ignore native types that are defined from here */ ] ,
2020-12-14 13:13:08 +03:00
disallowedDefinitions : [
'lib.dom.d.ts' ,
'@types/node' // no node.js
]
} ,
// Common: vs/platform/native/common/native.ts
{
target : '**/vs/platform/native/common/native.ts' ,
allowedTypes : CORE _TYPES ,
2022-02-08 22:09:00 +03:00
disallowedTypes : [ /* Ignore native types that are defined from here */ ] ,
2020-12-14 13:13:08 +03:00
disallowedDefinitions : [
'lib.dom.d.ts' ,
'@types/node' // no node.js
]
} ,
// Common: vs/workbench/api/common/extHostExtensionService.ts
{
target : '**/vs/workbench/api/common/extHostExtensionService.ts' ,
allowedTypes : [
... CORE _TYPES ,
// Safe access to global
'global'
] ,
disallowedTypes : NATIVE _TYPES ,
disallowedDefinitions : [
'lib.dom.d.ts' ,
'@types/node' // no node.js
]
} ,
// Common
{
target : '**/vs/**/common/**' ,
allowedTypes : CORE _TYPES ,
disallowedTypes : NATIVE _TYPES ,
disallowedDefinitions : [
'lib.dom.d.ts' ,
'@types/node' // no node.js
]
} ,
// Browser
{
target : '**/vs/**/browser/**' ,
allowedTypes : CORE _TYPES ,
disallowedTypes : NATIVE _TYPES ,
2022-03-10 18:51:37 +03:00
allowedDefinitions : [
'@types/node/stream/consumers.d.ts' // node.js started to duplicate types from lib.dom.d.ts so we have to account for that
] ,
2020-12-14 13:13:08 +03:00
disallowedDefinitions : [
'@types/node' // no node.js
]
} ,
// Browser (editor contrib)
{
target : '**/src/vs/editor/contrib/**' ,
allowedTypes : CORE _TYPES ,
disallowedTypes : NATIVE _TYPES ,
disallowedDefinitions : [
'@types/node' // no node.js
]
} ,
// node.js
{
target : '**/vs/**/node/**' ,
2022-04-04 16:04:29 +03:00
allowedTypes : CORE _TYPES ,
2020-12-14 13:13:08 +03:00
disallowedDefinitions : [
'lib.dom.d.ts' // no DOM
]
} ,
// Electron (sandbox)
{
target : '**/vs/**/electron-sandbox/**' ,
allowedTypes : CORE _TYPES ,
disallowedDefinitions : [
'@types/node' // no node.js
]
} ,
// Electron (renderer): skip
{
target : '**/vs/**/electron-browser/**' ,
skip : true // -> supports all types
} ,
// Electron (main)
{
target : '**/vs/**/electron-main/**' ,
allowedTypes : [
... CORE _TYPES ,
// --> types from electron.d.ts that duplicate from lib.dom.d.ts
'Event' ,
'Request'
] ,
disallowedDefinitions : [
'lib.dom.d.ts' // no DOM
]
}
] ;
2021-07-09 00:27:39 +03:00
const TS _CONFIG _PATH = ( 0 , path _1 . join ) ( _ _dirname , '../../' , 'src' , 'tsconfig.json' ) ;
2020-12-14 13:13:08 +03:00
let hasErrors = false ;
function checkFile ( program , sourceFile , rule ) {
checkNode ( sourceFile ) ;
function checkNode ( node ) {
if ( node . kind !== ts . SyntaxKind . Identifier ) {
return ts . forEachChild ( node , checkNode ) ; // recurse down
}
2022-04-04 16:04:29 +03:00
const checker = program . getTypeChecker ( ) ;
const symbol = checker . getSymbolAtLocation ( node ) ;
if ( ! symbol ) {
return ;
}
let _parentSymbol = symbol ;
while ( _parentSymbol . parent ) {
_parentSymbol = _parentSymbol . parent ;
}
const parentSymbol = _parentSymbol ;
const text = parentSymbol . getName ( ) ;
2022-03-01 01:00:17 +03:00
if ( rule . allowedTypes ? . some ( allowed => allowed === text ) ) {
2020-12-14 13:13:08 +03:00
return ; // override
}
2022-03-01 01:00:17 +03:00
if ( rule . disallowedTypes ? . some ( disallowed => disallowed === text ) ) {
2020-12-14 13:13:08 +03:00
const { line , character } = sourceFile . getLineAndCharacterOfPosition ( node . getStart ( ) ) ;
2022-04-04 16:04:29 +03:00
console . log ( ` [build/lib/layersChecker.ts]: Reference to type ' ${ text } ' violates layer ' ${ rule . target } ' ( ${ sourceFile . fileName } ( ${ line + 1 } , ${ character + 1 } ) ` ) ;
2020-12-14 13:13:08 +03:00
hasErrors = true ;
return ;
}
2022-04-04 16:04:29 +03:00
const declarations = symbol . declarations ;
if ( Array . isArray ( declarations ) ) {
DeclarationLoop : for ( const declaration of declarations ) {
if ( declaration ) {
const parent = declaration . parent ;
if ( parent ) {
const parentSourceFile = parent . getSourceFile ( ) ;
if ( parentSourceFile ) {
const definitionFileName = parentSourceFile . fileName ;
if ( rule . allowedDefinitions ) {
for ( const allowedDefinition of rule . allowedDefinitions ) {
if ( definitionFileName . indexOf ( allowedDefinition ) >= 0 ) {
continue DeclarationLoop ;
2022-02-08 22:09:00 +03:00
}
}
2022-04-04 16:04:29 +03:00
}
if ( rule . disallowedDefinitions ) {
for ( const disallowedDefinition of rule . disallowedDefinitions ) {
if ( definitionFileName . indexOf ( disallowedDefinition ) >= 0 ) {
const { line , character } = sourceFile . getLineAndCharacterOfPosition ( node . getStart ( ) ) ;
console . log ( ` [build/lib/layersChecker.ts]: Reference to symbol ' ${ text } ' from ' ${ disallowedDefinition } ' violates layer ' ${ rule . target } ' ( ${ sourceFile . fileName } ( ${ line + 1 } , ${ character + 1 } ) ` ) ;
hasErrors = true ;
return ;
2020-12-14 13:13:08 +03:00
}
}
}
}
}
}
}
}
}
}
function createProgram ( tsconfigPath ) {
const tsConfig = ts . readConfigFile ( tsconfigPath , ts . sys . readFile ) ;
2021-07-09 00:27:39 +03:00
const configHostParser = { fileExists : fs _1 . existsSync , readDirectory : ts . sys . readDirectory , readFile : file => ( 0 , fs _1 . readFileSync ) ( file , 'utf8' ) , useCaseSensitiveFileNames : process . platform === 'linux' } ;
const tsConfigParsed = ts . parseJsonConfigFileContent ( tsConfig . config , configHostParser , ( 0 , path _1 . resolve ) ( ( 0 , path _1 . dirname ) ( tsconfigPath ) ) , { noEmit : true } ) ;
2020-12-14 13:13:08 +03:00
const compilerHost = ts . createCompilerHost ( tsConfigParsed . options , true ) ;
return ts . createProgram ( tsConfigParsed . fileNames , tsConfigParsed . options , compilerHost ) ;
}
//
// Create program and start checking
//
const program = createProgram ( TS _CONFIG _PATH ) ;
for ( const sourceFile of program . getSourceFiles ( ) ) {
for ( const rule of RULES ) {
2021-07-09 00:27:39 +03:00
if ( ( 0 , minimatch _1 . match ) ( [ sourceFile . fileName ] , rule . target ) . length > 0 ) {
2020-12-14 13:13:08 +03:00
if ( ! rule . skip ) {
checkFile ( program , sourceFile , rule ) ;
}
break ;
}
}
}
if ( hasErrors ) {
process . exit ( 1 ) ;
}