test(language-service): test `@angular/language-service` can be loaded by tsserver.js (#14721)
This commit is contained in:
parent
79fc1e3959
commit
7a66a4115b
|
@ -0,0 +1 @@
|
||||||
|
*.js
|
|
@ -0,0 +1,36 @@
|
||||||
|
# Angular Language Service Test
|
||||||
|
|
||||||
|
This directory is an integration test for `@angular/language-service` to ensure
|
||||||
|
that various versions of the server can be loaded in the supported versions of
|
||||||
|
TypeScript's language service.
|
||||||
|
|
||||||
|
## New supported version of TypeScript
|
||||||
|
|
||||||
|
To add a new supported version of TypeScript:
|
||||||
|
|
||||||
|
1) Create directory in `typescripts` to hold the new version following the pattern
|
||||||
|
of the other versions.
|
||||||
|
2) Add the directory name to the end of the `TYPESCRIPTS` variable in the
|
||||||
|
`scripts/env.sh` file.
|
||||||
|
3) Run `scripts/update_golden.sh` to generate the expected files.
|
||||||
|
4) Verify the expected output is reasonable by comparing to a known good output
|
||||||
|
from a previous version.
|
||||||
|
|
||||||
|
## Update golden files
|
||||||
|
|
||||||
|
If the expected output needs to be updated run `scripts/update_golden.sh` to
|
||||||
|
update the expected output of the server.
|
||||||
|
|
||||||
|
## Adding a new fixture
|
||||||
|
|
||||||
|
Currently there is no automated way to produce a new fixture. The way the
|
||||||
|
current fixtures were created was to hack a version of tsserver.js to write the
|
||||||
|
commands from `VSCode` to a file while performing the operation to be tested.
|
||||||
|
I also hand modified the input to remove superfluous request.
|
||||||
|
|
||||||
|
Once a new fixture is created:
|
||||||
|
|
||||||
|
1) Add the fixture base name (without the .json) to `FIXTURES` in
|
||||||
|
`scripts/env.sh`.
|
||||||
|
2) Run `scripts/udpate_golden.sh` to produce the expected output files.
|
||||||
|
3) Hand validate the expected output is reasonable.
|
|
@ -0,0 +1,260 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "response",
|
||||||
|
"command": "configure",
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "response",
|
||||||
|
"command": "compilerOptionsForInferredProjects",
|
||||||
|
"success": true,
|
||||||
|
"body": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "response",
|
||||||
|
"command": "completions",
|
||||||
|
"success": true,
|
||||||
|
"body": [
|
||||||
|
{
|
||||||
|
"name": "anchor",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "anchor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "big",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "big"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "blink",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "blink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "bold",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "bold"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "charAt",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "charAt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "charCodeAt",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "charCodeAt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "codePointAt",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "codePointAt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "concat",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "concat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "endsWith",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "endsWith"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fixed",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "fixed"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fontcolor",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "fontcolor"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "fontsize",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "fontsize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "includes",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "includes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "indexOf",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "indexOf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "italics",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "italics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "lastIndexOf",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "lastIndexOf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "length",
|
||||||
|
"kind": "property",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "length"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "link",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "link"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "localeCompare",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "localeCompare"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "match",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "match"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "normalize",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "normalize"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "repeat",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "repeat"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "replace",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "replace"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "search",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "search"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "slice",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "slice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "small",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "small"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "split",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "split"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "startsWith",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "startsWith"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "strike",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "strike"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sub",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "sub"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "substr",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "substr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "substring",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "substring"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "sup",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "sup"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "toLocaleLowerCase",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "toLocaleLowerCase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "toLocaleUpperCase",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "toLocaleUpperCase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "toLowerCase",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "toLowerCase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "toString",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "toString"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "toUpperCase",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "toUpperCase"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "trim",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "trim"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "valueOf",
|
||||||
|
"kind": "method",
|
||||||
|
"kindModifiers": "",
|
||||||
|
"sortText": "valueOf"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,68 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"seq": 0,
|
||||||
|
"type": "request",
|
||||||
|
"command": "configure",
|
||||||
|
"arguments": {
|
||||||
|
"hostInfo": "vscode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 1,
|
||||||
|
"type": "request",
|
||||||
|
"command": "compilerOptionsForInferredProjects",
|
||||||
|
"arguments": {
|
||||||
|
"options": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES6",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowNonTsExtensions": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "Preserve"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 4,
|
||||||
|
"type": "request",
|
||||||
|
"command": "open",
|
||||||
|
"arguments": {
|
||||||
|
"file": "$$PWD$$/project/app/app.component.ts",
|
||||||
|
"fileContent": "import { Component } from '@angular/core';\n\n@Component({\n selector: 'my-app',\n template: `<h1>Hello {{name}}</h1>`,\n})\nexport class AppComponent { name = 'Angular'; }\n"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 7,
|
||||||
|
"type": "request",
|
||||||
|
"command": "geterr",
|
||||||
|
"arguments": {
|
||||||
|
"delay": 0,
|
||||||
|
"files": [
|
||||||
|
"$$PWD$$/project/app/app.component.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 12,
|
||||||
|
"type": "request",
|
||||||
|
"command": "change",
|
||||||
|
"arguments": {
|
||||||
|
"file": "$$PWD$$/project/app/app.component.ts",
|
||||||
|
"line": 5,
|
||||||
|
"offset": 30,
|
||||||
|
"endLine": 5,
|
||||||
|
"endOffset": 30,
|
||||||
|
"insertString": "."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 13,
|
||||||
|
"type": "request",
|
||||||
|
"command": "completions",
|
||||||
|
"arguments": {
|
||||||
|
"file": "$$PWD$$/project/app/app.component.ts",
|
||||||
|
"line": 5,
|
||||||
|
"offset": 31
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,13 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"type": "response",
|
||||||
|
"command": "configure",
|
||||||
|
"success": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "response",
|
||||||
|
"command": "compilerOptionsForInferredProjects",
|
||||||
|
"success": true,
|
||||||
|
"body": true
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,45 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"seq": 0,
|
||||||
|
"type": "request",
|
||||||
|
"command": "configure",
|
||||||
|
"arguments": {
|
||||||
|
"hostInfo": "vscode"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 1,
|
||||||
|
"type": "request",
|
||||||
|
"command": "compilerOptionsForInferredProjects",
|
||||||
|
"arguments": {
|
||||||
|
"options": {
|
||||||
|
"module": "CommonJS",
|
||||||
|
"target": "ES6",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"allowNonTsExtensions": true,
|
||||||
|
"allowJs": true,
|
||||||
|
"jsx": "Preserve"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 2,
|
||||||
|
"type": "request",
|
||||||
|
"command": "open",
|
||||||
|
"arguments": {
|
||||||
|
"file": "$$PWD$$/app/app.module.ts",
|
||||||
|
"fileContent": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"seq": 3,
|
||||||
|
"type": "request",
|
||||||
|
"command": "geterr",
|
||||||
|
"arguments": {
|
||||||
|
"delay": 0,
|
||||||
|
"files": [
|
||||||
|
"$$PWD$$/app/app.module.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"name": "language_service_plugin",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "MIT",
|
||||||
|
"decription": "Angular Langauge Service plugin integration test",
|
||||||
|
"dependencies": {
|
||||||
|
"@angular/common": "file:../../dist/packages-dist/common",
|
||||||
|
"@angular/compiler": "file:../../dist/packages-dist/compiler",
|
||||||
|
"@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli",
|
||||||
|
"@angular/core": "file:../../dist/packages-dist/core",
|
||||||
|
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||||
|
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||||
|
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||||
|
"@angular/tsc-wrapped": "file:../../dist/tools/@angular/tsc-wrapped",
|
||||||
|
"@types/minimist": "^1.2.0",
|
||||||
|
"@types/node": "^7.0.5",
|
||||||
|
"minimist": "^1.2.0",
|
||||||
|
"rxjs": "file:../../node_modules/rxjs",
|
||||||
|
"typescript": "^2.1.5",
|
||||||
|
"zone.js": "0.7.6"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"postinstall": "scripts/install.sh",
|
||||||
|
"test": "tsc -p tools && scripts/test.sh"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { Component } from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'my-app',
|
||||||
|
template: `<h1>Hello {{name}}</h1>`,
|
||||||
|
})
|
||||||
|
export class AppComponent { name = 'Angular'; }
|
|
@ -0,0 +1,11 @@
|
||||||
|
import { NgModule } from '@angular/core';
|
||||||
|
import { BrowserModule } from '@angular/platform-browser';
|
||||||
|
|
||||||
|
import { AppComponent } from './app.component';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
imports: [ BrowserModule ],
|
||||||
|
declarations: [ AppComponent ],
|
||||||
|
bootstrap: [ AppComponent ]
|
||||||
|
})
|
||||||
|
export class AppModule { }
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
|
||||||
|
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
platformBrowserDynamic().bootstrapModule(AppModule);
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"lib": [ "es2015", "dom" ],
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"suppressImplicitAnyIndexErrors": true,
|
||||||
|
"plugins": [
|
||||||
|
{ "name": "@angular/language-service" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
TYPESCRIPTS=2.3
|
||||||
|
FIXTURES="smokeTest getCompletions"
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex -o pipefail
|
||||||
|
|
||||||
|
cd `dirname $0`
|
||||||
|
cd ..
|
||||||
|
source scripts/env.sh
|
||||||
|
|
||||||
|
# Setup TypeScripts
|
||||||
|
for TYPESCRIPT in ${TYPESCRIPTS[@]}
|
||||||
|
do
|
||||||
|
(
|
||||||
|
cd typescripts/$TYPESCRIPT
|
||||||
|
yarn
|
||||||
|
)
|
||||||
|
done
|
|
@ -0,0 +1,25 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex -o pipefail
|
||||||
|
|
||||||
|
cd `dirname $0`
|
||||||
|
cd ..
|
||||||
|
source scripts/env.sh
|
||||||
|
|
||||||
|
HOST="node tools/typescript_host.js"
|
||||||
|
VALIDATE="node tools/typescript_validator.js"
|
||||||
|
|
||||||
|
for TYPESCRIPT in ${TYPESCRIPTS[@]}
|
||||||
|
do
|
||||||
|
SERVER="node typescripts/$TYPESCRIPT/node_modules/typescript/lib/tsserver.js"
|
||||||
|
for FIXTURE_BASE in ${FIXTURES[@]}
|
||||||
|
do
|
||||||
|
FIXTURE=fixtures/$FIXTURE_BASE.json
|
||||||
|
EXPECTED=fixtures/$FIXTURE_BASE-expected-$TYPESCRIPT.json
|
||||||
|
if [[ ${UPDATE_GOLDEN} == true ]]; then
|
||||||
|
$HOST --file $FIXTURE --pwd $(pwd) | $SERVER | $VALIDATE --golden > $EXPECTED
|
||||||
|
else
|
||||||
|
$HOST --file $FIXTURE --pwd $(pwd) | $SERVER | $VALIDATE --expect $EXPECTED
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -ex -o pipefail
|
||||||
|
|
||||||
|
cd `dirname $0`
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
UPDATE_GOLDEN=true scripts/test.sh
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": false,
|
||||||
|
"lib": ["es2015", "dom"],
|
||||||
|
"types": [
|
||||||
|
"node",
|
||||||
|
"minimist"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"typescript_host.ts",
|
||||||
|
"typescript_validator.ts"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as minimist from 'minimist';
|
||||||
|
|
||||||
|
const RE_PWD = /\$\$PWD\$\$/g;
|
||||||
|
|
||||||
|
let errorsDetected = false;
|
||||||
|
|
||||||
|
function reportError(arg: string): boolean {
|
||||||
|
console.error(`Unknown argument: ${arg}`);
|
||||||
|
errorsDetected = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function help() {
|
||||||
|
console.log('TypeScript Host')
|
||||||
|
console.log(`${process.argv[1]} --file <file-name> [--pwd <pwd>]`);
|
||||||
|
console.log(`
|
||||||
|
Send JSON message using the JSON RPC protocol to stdout.
|
||||||
|
`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = minimist(process.argv.slice(2), { string: ['file', 'pwd'], unknown: reportError });
|
||||||
|
|
||||||
|
if (errorsDetected) {
|
||||||
|
help();
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const file = args['file'];
|
||||||
|
if (!file) {
|
||||||
|
console.log('stdin form not supported yet.')
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sender
|
||||||
|
const pending: string[] = [];
|
||||||
|
let writing = false;
|
||||||
|
|
||||||
|
function writeMessage(message: string) {
|
||||||
|
writing = true;
|
||||||
|
process.stdout.write(message + '\n', checkPending);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPending() {
|
||||||
|
writing = false;
|
||||||
|
if (pending.length) {
|
||||||
|
writeMessage(pending.shift());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function send(message: string) {
|
||||||
|
if (writing) {
|
||||||
|
pending.push(message);
|
||||||
|
} else {
|
||||||
|
writeMessage(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let content = fs.readFileSync(file, 'utf8');
|
||||||
|
if (args['pwd']) {
|
||||||
|
content = content.replace(RE_PWD, args['pwd']);
|
||||||
|
}
|
||||||
|
|
||||||
|
const json = JSON.parse(content);
|
||||||
|
|
||||||
|
if (Array.isArray(json)) {
|
||||||
|
for (const message of json) {
|
||||||
|
send(JSON.stringify(message));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Error('Expected an array for input messages.')
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
console.error(`Error: ${e.message}`);
|
||||||
|
process.exit(2);
|
||||||
|
}
|
|
@ -0,0 +1,164 @@
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as minimist from 'minimist';
|
||||||
|
|
||||||
|
let errorsDetected = false;
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
|
||||||
|
function reportError(arg: string): boolean {
|
||||||
|
console.error(`Unknown argument: ${arg}`);
|
||||||
|
errorsDetected = true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function help() {
|
||||||
|
console.log('TypeScript Validator')
|
||||||
|
console.log(`${process.argv[1]} [--expect <file-name> | --golden] [--pwd <dir>]`);
|
||||||
|
console.log(`
|
||||||
|
Validate that the emitted output produces the expect JSON.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = minimist(process.argv.slice(2), { string: ['expect', 'pwd'], boolean: ['golden'], unknown: reportError });
|
||||||
|
|
||||||
|
if (!args.golden && !args.expect) {
|
||||||
|
console.log('Expected -golden or -expect');
|
||||||
|
errorsDetected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.golden && args.expect) {
|
||||||
|
console.log('Expected -golded or -expect but not both');
|
||||||
|
errorsDetected = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorsDetected) {
|
||||||
|
help();
|
||||||
|
process.exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var expected: any;
|
||||||
|
if (args.expect) {
|
||||||
|
expected = JSON.parse(fs.readFileSync(args.expect, 'utf8'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reader
|
||||||
|
let pending = Buffer.alloc(0);
|
||||||
|
|
||||||
|
const prefix = 'Content-Length: ';
|
||||||
|
|
||||||
|
function tryReadMessage(cb: (message: any) => void) {
|
||||||
|
const firstLine = pending.indexOf(10);
|
||||||
|
if (firstLine >= 1) {
|
||||||
|
const line = pending.toString('utf8', 0, firstLine);
|
||||||
|
if (!line.startsWith(prefix)) {
|
||||||
|
throw Error(`Unexpected input: ${line}`);
|
||||||
|
}
|
||||||
|
const length = +line.substring(prefix.length, firstLine - 1);
|
||||||
|
const dataStart = firstLine + 2;
|
||||||
|
const messageText = pending.toString('utf8', dataStart, dataStart + length);
|
||||||
|
const message = JSON.parse(messageText);
|
||||||
|
pending = pending.slice(dataStart + length + 1);
|
||||||
|
cb(message);
|
||||||
|
tryReadMessage(cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function collect(cb: (error: any, messages: any[]) => void) {
|
||||||
|
const result: any[] = [];
|
||||||
|
|
||||||
|
function report(error: any, messages: any[]) {
|
||||||
|
cb(error, messages);
|
||||||
|
cb = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
process.stdin.on('error', report);
|
||||||
|
process.stdin.on('data', (data: Buffer) => {
|
||||||
|
try {
|
||||||
|
pending = Buffer.concat([pending, data], pending.length + data.length);
|
||||||
|
tryReadMessage((message: any) => {
|
||||||
|
result.push(message);
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
report(e, []);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
process.stdin.on('close', () => {
|
||||||
|
report(null, result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitize(messages: any[]): any[] {
|
||||||
|
return messages.filter((message: any) => {
|
||||||
|
return message && message.type == 'response';
|
||||||
|
}).map((message: any) => {
|
||||||
|
// Only preserve a fixed set of fields.
|
||||||
|
const result: any = {};
|
||||||
|
if (message.type != null) result.type = message.type;
|
||||||
|
if (message.command != null) result.command = message.command;
|
||||||
|
if (message.success != null) result.success = message.success;
|
||||||
|
if (message.body != null) result.body = message.body;
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isPrimitive(value: any): boolean {
|
||||||
|
return Object(value) !== value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectPrimitive(received: any, expected: any) {
|
||||||
|
if (received !== expected) {
|
||||||
|
throw new Error(`Expected ${expected} but received ${received}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectArray(received: any, expected: any[]) {
|
||||||
|
if (!Array.isArray(received)) {
|
||||||
|
throw new Error(`Expected an array, received ${JSON.stringify(received)}`);
|
||||||
|
}
|
||||||
|
if (received.length != expected.length) {
|
||||||
|
throw new Error(`Expected an array length ${expected.length}, received ${JSON.stringify(received)}`);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < expected.length; i++) {
|
||||||
|
expect(received[i], expected[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expectObject(received: any, expected: any) {
|
||||||
|
for (const name of Object.getOwnPropertyNames(expected)) {
|
||||||
|
if (!received.hasOwnProperty(name)) {
|
||||||
|
throw new Error(`Expected object an object containing a field ${name}, received ${JSON.stringify(expected)}`);
|
||||||
|
}
|
||||||
|
expect(received[name], expected[name]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function expect(received: any, expected: any) {
|
||||||
|
if (isPrimitive(expected)) {
|
||||||
|
expectPrimitive(received, expected);
|
||||||
|
} else if (Array.isArray(expected)) {
|
||||||
|
expectArray(received, expected);
|
||||||
|
} else {
|
||||||
|
expectObject(received, expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
collect((err: any, messages: any[]) => {
|
||||||
|
if (err) {
|
||||||
|
console.error(err.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
if (args.golden) {
|
||||||
|
console.log(JSON.stringify(sanitize(messages), null, ' '));
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
expect(sanitize(messages), expected);
|
||||||
|
console.log('PASSED:', Date.now() - start, 'ms');
|
||||||
|
process.exit(0);
|
||||||
|
} catch(e) {
|
||||||
|
console.log('FAILED:', e.message);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
|
@ -0,0 +1,13 @@
|
||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"sourceMap": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"lib": [ "es2015", "dom" ],
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"suppressImplicitAnyIndexErrors": true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
{
|
||||||
|
"name": "2.3",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"typescript": "2.3.0-dev.20170223"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||||
|
# yarn lockfile v1
|
||||||
|
|
||||||
|
|
||||||
|
typescript@2.3.0-dev.20170223:
|
||||||
|
version "2.3.0-dev.20170223"
|
||||||
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.0-dev.20170223.tgz#286494c36625ea2eb26f963ed205cd9ca5c41447"
|
Loading…
Reference in New Issue