build: remove references to tsc-wrapped
(#19298)
With this commit `ngc` is used instead of `tsc-wrapped` for collecting metadata and tsickle rewriting and `tsc-wrapped` is removed from the repository. `@angular/tsc-wrapped@5` is now deprecated and is no longer used, updated, or maintained as part as of Angular 5.x.x. `@angular/tsc-wrapped@4` is still maintained and required by Angular 4.x.x and will be maintained as long as 4.x.x is in LTS. PR Close #19298
This commit is contained in:
parent
4c73b52d5c
commit
f96142cd7c
@ -135,7 +135,6 @@ groups:
|
|||||||
compiler-cli:
|
compiler-cli:
|
||||||
conditions:
|
conditions:
|
||||||
files:
|
files:
|
||||||
- "packages/tsc-wrapped/*"
|
|
||||||
- "packages/compiler-cli/*"
|
- "packages/compiler-cli/*"
|
||||||
- "packages/bazel/*"
|
- "packages/bazel/*"
|
||||||
users:
|
users:
|
||||||
|
@ -221,7 +221,6 @@ The following is the list of supported scopes:
|
|||||||
* **platform-webworker-dynamic**
|
* **platform-webworker-dynamic**
|
||||||
* **router**
|
* **router**
|
||||||
* **upgrade**
|
* **upgrade**
|
||||||
* **tsc-wrapped**
|
|
||||||
|
|
||||||
There are currently a few exceptions to the "use package name" rule:
|
There are currently a few exceptions to the "use package name" rule:
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ You can think of `.metadata.json` as a diagram of the overall structure of a dec
|
|||||||
|
|
||||||
<div class="l-sub-section">
|
<div class="l-sub-section">
|
||||||
|
|
||||||
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/tsc-wrapped/src/schema.ts)
|
Angular's [schema.ts](https://github.com/angular/angular/blob/master/packages/compiler-cli/src/metadata/schema.ts)
|
||||||
describes the JSON format as a collection of TypeScript interfaces.
|
describes the JSON format as a collection of TypeScript interfaces.
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
"@angular/platform-browser-dynamic",
|
"@angular/platform-browser-dynamic",
|
||||||
"@angular/platform-server",
|
"@angular/platform-server",
|
||||||
"@angular/router",
|
"@angular/router",
|
||||||
"@angular/tsc-wrapped",
|
|
||||||
"@angular/upgrade",
|
"@angular/upgrade",
|
||||||
"angular-in-memory-web-api",
|
"angular-in-memory-web-api",
|
||||||
"core-js",
|
"core-js",
|
||||||
|
@ -41,7 +41,6 @@ const ANGULAR_PACKAGES = [
|
|||||||
'platform-browser-dynamic',
|
'platform-browser-dynamic',
|
||||||
'platform-server',
|
'platform-server',
|
||||||
'router',
|
'router',
|
||||||
'tsc-wrapped',
|
|
||||||
'upgrade',
|
'upgrade',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -26,13 +26,11 @@ describe('example-boilerplate tool', () => {
|
|||||||
|
|
||||||
it('should override the Angular node_modules with the locally built Angular packages if `useLocal` is true', () => {
|
it('should override the Angular node_modules with the locally built Angular packages if `useLocal` is true', () => {
|
||||||
const numberOfAngularPackages = 12;
|
const numberOfAngularPackages = 12;
|
||||||
const numberOfAngularToolsPackages = 1;
|
|
||||||
exampleBoilerPlate.add(true);
|
exampleBoilerPlate.add(true);
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledTimes(numberOfAngularPackages + numberOfAngularToolsPackages);
|
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledTimes(numberOfAngularPackages);
|
||||||
// for example
|
// for example
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
|
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'common');
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
|
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'core');
|
||||||
expect(exampleBoilerPlate.overridePackage).toHaveBeenCalledWith(path.resolve(__dirname, '../../../dist/packages-dist'), 'tsc-wrapped');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should process all the example folders', () => {
|
it('should process all the example folders', () => {
|
||||||
|
@ -23,7 +23,6 @@
|
|||||||
"@angular/platform-browser-dynamic": "~4.3.1",
|
"@angular/platform-browser-dynamic": "~4.3.1",
|
||||||
"@angular/platform-server": "~4.3.1",
|
"@angular/platform-server": "~4.3.1",
|
||||||
"@angular/router": "~4.3.1",
|
"@angular/router": "~4.3.1",
|
||||||
"@angular/tsc-wrapped": "~4.3.1",
|
|
||||||
"@angular/upgrade": "~4.3.1",
|
"@angular/upgrade": "~4.3.1",
|
||||||
"angular-in-memory-web-api": "~0.4.0",
|
"angular-in-memory-web-api": "~0.4.0",
|
||||||
"core-js": "^2.4.1",
|
"core-js": "^2.4.1",
|
||||||
|
43
build.sh
43
build.sh
@ -25,9 +25,12 @@ PACKAGES=(core
|
|||||||
language-service
|
language-service
|
||||||
benchpress)
|
benchpress)
|
||||||
|
|
||||||
|
TSC_PACKAGES=(compiler-cli
|
||||||
|
language-service
|
||||||
|
benchpress)
|
||||||
|
|
||||||
NODE_PACKAGES=(compiler-cli
|
NODE_PACKAGES=(compiler-cli
|
||||||
benchpress
|
benchpress)
|
||||||
tsc-wrapped)
|
|
||||||
|
|
||||||
BUILD_ALL=true
|
BUILD_ALL=true
|
||||||
BUNDLE=true
|
BUNDLE=true
|
||||||
@ -38,6 +41,7 @@ BUILD_EXAMPLES=true
|
|||||||
COMPILE_SOURCE=true
|
COMPILE_SOURCE=true
|
||||||
TYPECHECK_ALL=true
|
TYPECHECK_ALL=true
|
||||||
BUILD_TOOLS=true
|
BUILD_TOOLS=true
|
||||||
|
export NODE_PATH=${NODE_PATH:-}:${currentDir}/dist/tools
|
||||||
|
|
||||||
for ARG in "$@"; do
|
for ARG in "$@"; do
|
||||||
case "$ARG" in
|
case "$ARG" in
|
||||||
@ -156,7 +160,7 @@ runRollup() {
|
|||||||
if [[ -f "${1}/rollup.config.js" ]]; then
|
if [[ -f "${1}/rollup.config.js" ]]; then
|
||||||
cd ${1}
|
cd ${1}
|
||||||
|
|
||||||
echo "====== $ROLLUP -c ${1}/rollup.config.js"
|
echo "====== $ROLLUP -c ${1}/rollup.config.js --sourcemap"
|
||||||
$ROLLUP -c rollup.config.js --sourcemap >/dev/null 2>&1
|
$ROLLUP -c rollup.config.js --sourcemap >/dev/null 2>&1
|
||||||
|
|
||||||
# Recurse for sub directories
|
# Recurse for sub directories
|
||||||
@ -216,11 +220,12 @@ minify() {
|
|||||||
# None
|
# None
|
||||||
#######################################
|
#######################################
|
||||||
compilePackage() {
|
compilePackage() {
|
||||||
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json"
|
# For TSC_PACKAGES items
|
||||||
# For NODE_PACKAGES items (not getting rolled up)
|
if containsElement "${3}" "${TSC_PACKAGES[@]}"; then
|
||||||
if containsElement "${3}" "${NODE_PACKAGES[@]}"; then
|
echo "====== [${3}]: COMPILING: ${TSC} -p ${1}/tsconfig-build.json"
|
||||||
$TSC -p ${1}/tsconfig-build.json
|
$TSC -p ${1}/tsconfig-build.json
|
||||||
else
|
else
|
||||||
|
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json"
|
||||||
local package_name=$(basename "${2}")
|
local package_name=$(basename "${2}")
|
||||||
$NGC -p ${1}/tsconfig-build.json
|
$NGC -p ${1}/tsconfig-build.json
|
||||||
echo "====== Create ${1}/../${package_name}.d.ts re-export file for tsickle"
|
echo "====== Create ${1}/../${package_name}.d.ts re-export file for tsickle"
|
||||||
@ -247,9 +252,15 @@ compilePackage() {
|
|||||||
# None
|
# None
|
||||||
#######################################
|
#######################################
|
||||||
compilePackageES5() {
|
compilePackageES5() {
|
||||||
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json --target es5 -d false --outDir ${2} --importHelpers true --sourceMap"
|
if containsElement "${3}" "${TSC_PACKAGES[@]}"; then
|
||||||
local package_name=$(basename "${2}")
|
echo "====== [${3}]: COMPILING: ${TSC} -p ${1}/tsconfig-build.json --target es5 -d false --outDir ${2} --importHelpers true --sourceMap"
|
||||||
$NGC -p ${1}/tsconfig-build.json --target es5 -d false --outDir ${2} --importHelpers true --sourceMap
|
local package_name=$(basename "${2}")
|
||||||
|
$TSC -p ${1}/tsconfig-build.json --target es5 -d false --outDir ${2} --importHelpers true --sourceMap
|
||||||
|
else
|
||||||
|
echo "====== [${3}]: COMPILING: ${NGC} -p ${1}/tsconfig-build.json --target es5 -d false --outDir ${2} --importHelpers true --sourceMap"
|
||||||
|
local package_name=$(basename "${2}")
|
||||||
|
$NGC -p ${1}/tsconfig-build.json --target es5 -d false --outDir ${2} --importHelpers true --sourceMap
|
||||||
|
fi
|
||||||
|
|
||||||
for DIR in ${1}/* ; do
|
for DIR in ${1}/* ; do
|
||||||
[ -d "${DIR}" ] || continue
|
[ -d "${DIR}" ] || continue
|
||||||
@ -315,7 +326,7 @@ echo "====== BUILDING: Version ${VERSION}"
|
|||||||
N="
|
N="
|
||||||
"
|
"
|
||||||
TSC=`pwd`/node_modules/.bin/tsc
|
TSC=`pwd`/node_modules/.bin/tsc
|
||||||
NGC="node --max-old-space-size=3000 `pwd`/dist/packages-dist/tsc-wrapped/src/main"
|
NGC="node --max-old-space-size=3000 `pwd`/dist/tools/@angular/compiler-cli/src/main"
|
||||||
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
UGLIFYJS=`pwd`/node_modules/.bin/uglifyjs
|
||||||
TSCONFIG=./tools/tsconfig.json
|
TSCONFIG=./tools/tsconfig.json
|
||||||
ROLLUP=`pwd`/node_modules/.bin/rollup
|
ROLLUP=`pwd`/node_modules/.bin/rollup
|
||||||
@ -394,14 +405,12 @@ if [[ ${BUILD_ALL} == true ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then
|
if [[ ${BUILD_TOOLS} == true || ${BUILD_ALL} == true ]]; then
|
||||||
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json ====="
|
echo "====== (compiler)COMPILING: \$(npm bin)/tsc -p packages/compiler/tsconfig-tools.json"
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json
|
$(npm bin)/tsc -p packages/compiler/tsconfig-tools.json
|
||||||
echo "====== (tsc-wrapped)COMPILING: \$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json ====="
|
echo "====== (compiler)COMPILING: \$(npm bin)/tsc -p packages/compiler-cli/tsconfig-tools.json"
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json
|
$(npm bin)/tsc -p packages/compiler-cli/tsconfig-tools.json
|
||||||
cp ./packages/tsc-wrapped/package.json ./dist/packages-dist/tsc-wrapped
|
|
||||||
cp ./packages/tsc-wrapped/README.md ./dist/packages-dist/tsc-wrapped
|
|
||||||
updateVersionReferences dist/packages-dist/tsc-wrapped
|
|
||||||
|
|
||||||
|
mkdir -p ./dist/packages-dist
|
||||||
rsync -a packages/bazel/ ./dist/packages-dist/bazel
|
rsync -a packages/bazel/ ./dist/packages-dist/bazel
|
||||||
# Remove BEGIN-INTERNAL...END-INTERAL blocks
|
# Remove BEGIN-INTERNAL...END-INTERAL blocks
|
||||||
# https://stackoverflow.com/questions/24175271/how-can-i-match-multi-line-patterns-in-the-command-line-with-perl-style-regex
|
# https://stackoverflow.com/questions/24175271/how-can-i-match-multi-line-patterns-in-the-command-line-with-perl-style-regex
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
"@angular/core": "file:../../dist/packages-dist/core",
|
"@angular/core": "file:../../dist/packages-dist/core",
|
||||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
|
||||||
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
|
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
|
||||||
"rxjs": "file:../../node_modules/rxjs",
|
"rxjs": "file:../../node_modules/rxjs",
|
||||||
"typescript": "file:../../node_modules/typescript",
|
"typescript": "file:../../node_modules/typescript",
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
"@angular/core": "file:../../dist/packages-dist/core",
|
"@angular/core": "file:../../dist/packages-dist/core",
|
||||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
|
||||||
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
|
"google-closure-compiler": "git+https://github.com/alexeagle/closure-compiler.git#packagejson.dist",
|
||||||
"rxjs": "file:../../node_modules/rxjs",
|
"rxjs": "file:../../node_modules/rxjs",
|
||||||
"typescript": "file:../../node_modules/typescript",
|
"typescript": "file:../../node_modules/typescript",
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
"@angular/language-service": "file:../../dist/packages-dist/language-service",
|
||||||
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
"@angular/platform-browser": "file:../../dist/packages-dist/platform-browser",
|
||||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
|
||||||
"@types/minimist": "^1.2.0",
|
"@types/minimist": "^1.2.0",
|
||||||
"@types/node": "^7.0.5",
|
"@types/node": "^7.0.5",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
"@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic",
|
||||||
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
"@angular/platform-server": "file:../../dist/packages-dist/platform-server",
|
||||||
"@angular/router": "file:../../dist/packages-dist/router",
|
"@angular/router": "file:../../dist/packages-dist/router",
|
||||||
"@angular/tsc-wrapped": "file:../../dist/packages-dist/tsc-wrapped",
|
|
||||||
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
"@angular/upgrade": "file:../../dist/packages-dist/upgrade",
|
||||||
"@types/jasmine": "2.5.41",
|
"@types/jasmine": "2.5.41",
|
||||||
"rxjs": "file:../../node_modules/rxjs",
|
"rxjs": "file:../../node_modules/rxjs",
|
||||||
|
@ -61,7 +61,6 @@ module.exports = function(config) {
|
|||||||
'dist/all/@angular/examples/**/e2e_test/*',
|
'dist/all/@angular/examples/**/e2e_test/*',
|
||||||
'dist/all/@angular/language-service/**',
|
'dist/all/@angular/language-service/**',
|
||||||
'dist/all/@angular/router/test/**',
|
'dist/all/@angular/router/test/**',
|
||||||
'dist/all/@angular/tsc-wrapped/**',
|
|
||||||
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
'dist/all/@angular/platform-browser/testing/e2e_util.js',
|
||||||
'dist/all/angular1_router.js',
|
'dist/all/angular1_router.js',
|
||||||
'dist/examples/**/e2e_test/**',
|
'dist/examples/**/e2e_test/**',
|
||||||
|
@ -12,9 +12,7 @@
|
|||||||
"paths": {
|
"paths": {
|
||||||
"selenium-webdriver": ["../node_modules/@types/selenium-webdriver/index.d.ts"],
|
"selenium-webdriver": ["../node_modules/@types/selenium-webdriver/index.d.ts"],
|
||||||
"rxjs/*": ["../node_modules/rxjs/*"],
|
"rxjs/*": ["../node_modules/rxjs/*"],
|
||||||
"@angular/*": ["../dist/all/@angular/*"],
|
"@angular/*": ["../dist/all/@angular/*"]
|
||||||
"@angular/tsc-wrapped": ["../dist/packages-dist/tsc-wrapped"],
|
|
||||||
"@angular/tsc-wrapped/*": ["../dist/packages-dist/tsc-wrapped/*"]
|
|
||||||
},
|
},
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"inlineSourceMap": true,
|
"inlineSourceMap": true,
|
||||||
@ -32,5 +30,8 @@
|
|||||||
"benchmarks_external",
|
"benchmarks_external",
|
||||||
"payload_tests",
|
"payload_tests",
|
||||||
"rollup-test"
|
"rollup-test"
|
||||||
]
|
],
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"skipTemplateCodegen": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": "../../",
|
"rootDir": "../../",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/animations": ["../../../../dist/packages/animations"]
|
"@angular/animations": ["../../../../dist/packages/animations"],
|
||||||
|
"@angular/core": ["../../../../dist/packages/core"]
|
||||||
},
|
},
|
||||||
"outDir": "../../../../dist/packages/animations"
|
"outDir": "../../../../dist/packages/animations"
|
||||||
},
|
},
|
||||||
@ -19,7 +20,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/animations/browser/testing"
|
"flatModuleId": "@angular/animations/browser/testing"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": "../",
|
"rootDir": "../",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/animations": ["../../../dist/packages/animations"]
|
"@angular/animations": ["../../../dist/packages/animations"],
|
||||||
|
"@angular/core": ["../../../dist/packages/core"]
|
||||||
},
|
},
|
||||||
"outDir": "../../../dist/packages/animations"
|
"outDir": "../../../dist/packages/animations"
|
||||||
},
|
},
|
||||||
@ -18,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "browser.js",
|
"flatModuleOutFile": "browser.js",
|
||||||
"flatModuleId": "@angular/animations/browser"
|
"flatModuleId": "@angular/animations/browser"
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,9 @@
|
|||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"paths": {},
|
"paths": {
|
||||||
|
"@angular/core": ["../../dist/packages/core"]
|
||||||
|
},
|
||||||
"outDir": "../../dist/packages/animations"
|
"outDir": "../../dist/packages/animations"
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -16,7 +18,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "animations.js",
|
"flatModuleOutFile": "animations.js",
|
||||||
"flatModuleId": "@angular/animations"
|
"flatModuleId": "@angular/animations"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/common/http/testing"
|
"flatModuleId": "@angular/common/http/testing"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "http.js",
|
"flatModuleOutFile": "http.js",
|
||||||
"flatModuleId": "@angular/common/http"
|
"flatModuleId": "@angular/common/http"
|
||||||
}
|
}
|
||||||
|
@ -19,5 +19,8 @@
|
|||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./closure-locale.ts"
|
"./closure-locale.ts"
|
||||||
]
|
],
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"skipTemplateCodegen": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/common/testing"
|
"flatModuleId": "@angular/common/testing"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "common.js",
|
"flatModuleOutFile": "common.js",
|
||||||
"flatModuleId": "@angular/common"
|
"flatModuleId": "@angular/common"
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,7 @@ ts_library(
|
|||||||
]),
|
]),
|
||||||
module_name = "@angular/compiler-cli",
|
module_name = "@angular/compiler-cli",
|
||||||
deps = [
|
deps = [
|
||||||
"//packages/compiler",
|
"//packages/compiler"
|
||||||
"//packages/tsc-wrapped",
|
|
||||||
],
|
],
|
||||||
tsconfig = ":tsconfig-build.json",
|
tsconfig = ":tsconfig-build.json",
|
||||||
)
|
)
|
||||||
|
@ -112,7 +112,7 @@ with the one already used in the plugin for TypeScript typechecking and emit.
|
|||||||
|
|
||||||
## Design
|
## Design
|
||||||
At a high level, this program
|
At a high level, this program
|
||||||
- collects static metadata about the sources using the `tsc-wrapped` package
|
- collects static metadata about the sources
|
||||||
- uses the `OfflineCompiler` from `@angular/compiler` to codegen additional `.ts` files
|
- uses the `OfflineCompiler` from `@angular/compiler` to codegen additional `.ts` files
|
||||||
- these `.ts` files are written to the `genDir` path, then compiled together with the application.
|
- these `.ts` files are written to the `genDir` path, then compiled together with the application.
|
||||||
|
|
||||||
@ -130,7 +130,7 @@ $ ./scripts/ci/offline_compiler_test.sh
|
|||||||
|
|
||||||
# Recompile @angular/core module (needs to use tsc-ext to keep the metadata)
|
# Recompile @angular/core module (needs to use tsc-ext to keep the metadata)
|
||||||
$ export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
$ export NODE_PATH=${NODE_PATH}:$(pwd)/dist/all:$(pwd)/dist/tools
|
||||||
$ node dist/tools/@angular/tsc-wrapped/src/main -p packages/core/tsconfig-build.json
|
$ node dist/tools/@angular/compiler-cli/src/main -p packages/core/tsconfig-build.json
|
||||||
|
|
||||||
# Iterate on the test
|
# Iterate on the test
|
||||||
$ cd /tmp/wherever/e2e_test.1464388257/
|
$ cd /tmp/wherever/e2e_test.1464388257/
|
||||||
|
@ -17,10 +17,7 @@ var rxjsLocation = normalize('../../node_modules/rxjs');
|
|||||||
var tslibLocation = normalize('../../node_modules/tslib');
|
var tslibLocation = normalize('../../node_modules/tslib');
|
||||||
var esm = 'esm/';
|
var esm = 'esm/';
|
||||||
|
|
||||||
var locations = {
|
var locations = {'compiler-cli': normalize('../../dist/packages') + '/'};
|
||||||
'tsc-wrapped': normalize('../../dist/tools/@angular') + '/',
|
|
||||||
'compiler-cli': normalize('../../dist/packages') + '/'
|
|
||||||
};
|
|
||||||
|
|
||||||
var esm_suffixes = {};
|
var esm_suffixes = {};
|
||||||
|
|
||||||
@ -30,11 +27,6 @@ function normalize(fileName) {
|
|||||||
|
|
||||||
function resolve(id, from) {
|
function resolve(id, from) {
|
||||||
// console.log('Resolve id:', id, 'from', from)
|
// console.log('Resolve id:', id, 'from', from)
|
||||||
if (id == '@angular/tsc-wrapped') {
|
|
||||||
// Hack to restrict the import to not include the index of @angular/tsc-wrapped so we don't
|
|
||||||
// rollup tsickle.
|
|
||||||
return locations['tsc-wrapped'] + 'tsc-wrapped/src/collector.js';
|
|
||||||
}
|
|
||||||
var match = m.exec(id);
|
var match = m.exec(id);
|
||||||
if (match) {
|
if (match) {
|
||||||
var packageName = match[1];
|
var packageName = match[1];
|
||||||
|
@ -14,8 +14,7 @@ import 'reflect-metadata';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import {tsc} from '@angular/tsc-wrapped/src/tsc';
|
import {__NGTOOLS_PRIVATE_API_2, readConfiguration} from '@angular/compiler-cli';
|
||||||
import {__NGTOOLS_PRIVATE_API_2} from '@angular/compiler-cli';
|
|
||||||
|
|
||||||
const glob = require('glob');
|
const glob = require('glob');
|
||||||
|
|
||||||
@ -50,14 +49,14 @@ function codeGenTest(forceError = false) {
|
|||||||
const readResources: string[] = [];
|
const readResources: string[] = [];
|
||||||
const wroteFiles: string[] = [];
|
const wroteFiles: string[] = [];
|
||||||
|
|
||||||
const config = tsc.readConfiguration(project, basePath);
|
const config = readConfiguration(project);
|
||||||
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
|
const delegateHost = ts.createCompilerHost(config.options, true);
|
||||||
const host: ts.CompilerHost = Object.assign(
|
const host: ts.CompilerHost = Object.assign(
|
||||||
{}, delegateHost,
|
{}, delegateHost,
|
||||||
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
||||||
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||||
|
|
||||||
config.ngOptions.basePath = basePath;
|
config.options.basePath = basePath;
|
||||||
|
|
||||||
console.log(`>>> running codegen for ${project}`);
|
console.log(`>>> running codegen for ${project}`);
|
||||||
if (forceError) {
|
if (forceError) {
|
||||||
@ -66,9 +65,9 @@ function codeGenTest(forceError = false) {
|
|||||||
return __NGTOOLS_PRIVATE_API_2
|
return __NGTOOLS_PRIVATE_API_2
|
||||||
.codeGen({
|
.codeGen({
|
||||||
basePath,
|
basePath,
|
||||||
compilerOptions: config.parsed.options, program, host,
|
compilerOptions: config.options, program, host,
|
||||||
|
|
||||||
angularCompilerOptions: config.ngOptions,
|
angularCompilerOptions: config.options,
|
||||||
|
|
||||||
// i18n options.
|
// i18n options.
|
||||||
i18nFormat: 'xlf',
|
i18nFormat: 'xlf',
|
||||||
@ -129,21 +128,21 @@ function i18nTest() {
|
|||||||
const readResources: string[] = [];
|
const readResources: string[] = [];
|
||||||
const wroteFiles: string[] = [];
|
const wroteFiles: string[] = [];
|
||||||
|
|
||||||
const config = tsc.readConfiguration(project, basePath);
|
const config = readConfiguration(project);
|
||||||
const delegateHost = ts.createCompilerHost(config.parsed.options, true);
|
const delegateHost = ts.createCompilerHost(config.options, true);
|
||||||
const host: ts.CompilerHost = Object.assign(
|
const host: ts.CompilerHost = Object.assign(
|
||||||
{}, delegateHost,
|
{}, delegateHost,
|
||||||
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
{writeFile: (fileName: string, ...rest: any[]) => { wroteFiles.push(fileName); }});
|
||||||
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||||
|
|
||||||
config.ngOptions.basePath = basePath;
|
config.options.basePath = basePath;
|
||||||
|
|
||||||
console.log(`>>> running i18n extraction for ${project}`);
|
console.log(`>>> running i18n extraction for ${project}`);
|
||||||
return __NGTOOLS_PRIVATE_API_2
|
return __NGTOOLS_PRIVATE_API_2
|
||||||
.extractI18n({
|
.extractI18n({
|
||||||
basePath,
|
basePath,
|
||||||
compilerOptions: config.parsed.options, program, host,
|
compilerOptions: config.options, program, host,
|
||||||
angularCompilerOptions: config.ngOptions,
|
angularCompilerOptions: config.options,
|
||||||
i18nFormat: 'xlf',
|
i18nFormat: 'xlf',
|
||||||
locale: undefined,
|
locale: undefined,
|
||||||
outFile: undefined,
|
outFile: undefined,
|
||||||
@ -193,18 +192,14 @@ function lazyRoutesTest() {
|
|||||||
const basePath = path.join(__dirname, '../ngtools_src');
|
const basePath = path.join(__dirname, '../ngtools_src');
|
||||||
const project = path.join(basePath, 'tsconfig-build.json');
|
const project = path.join(basePath, 'tsconfig-build.json');
|
||||||
|
|
||||||
const config = tsc.readConfiguration(project, basePath);
|
const config = readConfiguration(project);
|
||||||
const host = ts.createCompilerHost(config.parsed.options, true);
|
const host = ts.createCompilerHost(config.options, true);
|
||||||
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||||
|
|
||||||
config.ngOptions.basePath = basePath;
|
config.options.basePath = basePath;
|
||||||
|
|
||||||
const lazyRoutes = __NGTOOLS_PRIVATE_API_2.listLazyRoutes({
|
const lazyRoutes = __NGTOOLS_PRIVATE_API_2.listLazyRoutes(
|
||||||
program,
|
{program, host, angularCompilerOptions: config.options, entryModule: 'app.module#AppModule'});
|
||||||
host,
|
|
||||||
angularCompilerOptions: config.ngOptions,
|
|
||||||
entryModule: 'app.module#AppModule'
|
|
||||||
});
|
|
||||||
|
|
||||||
const expectations: {[route: string]: string} = {
|
const expectations: {[route: string]: string} = {
|
||||||
'./lazy.module#LazyModule': 'lazy.module.ts',
|
'./lazy.module#LazyModule': 'lazy.module.ts',
|
||||||
|
@ -14,8 +14,7 @@ import 'reflect-metadata';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
import * as assert from 'assert';
|
import * as assert from 'assert';
|
||||||
import {tsc} from '@angular/tsc-wrapped/src/tsc';
|
import {CompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext, readConfiguration} from '@angular/compiler-cli';
|
||||||
import {CompilerOptions, CodeGenerator, CompilerHostContext, NodeCompilerHostContext} from '@angular/compiler-cli';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main method.
|
* Main method.
|
||||||
@ -46,10 +45,10 @@ function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = tsc.readConfiguration(project, basePath);
|
const config = readConfiguration(project);
|
||||||
config.ngOptions.basePath = basePath;
|
config.options.basePath = basePath;
|
||||||
// This flag tells ngc do not recompile libraries.
|
// This flag tells ngc do not recompile libraries.
|
||||||
config.ngOptions.generateCodeForLibraries = false;
|
config.options.generateCodeForLibraries = false;
|
||||||
|
|
||||||
console.log(`>>> running codegen for ${project}`);
|
console.log(`>>> running codegen for ${project}`);
|
||||||
codegen(
|
codegen(
|
||||||
@ -86,21 +85,21 @@ function main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple adaption of tsc-wrapped main to just run codegen with a CompilerHostContext
|
* Simple adaption of main to just run codegen with a CompilerHostContext
|
||||||
*/
|
*/
|
||||||
function codegen(
|
function codegen(
|
||||||
config: {parsed: ts.ParsedCommandLine, ngOptions: CompilerOptions},
|
config: {options: CompilerOptions, rootNames: string[]},
|
||||||
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
|
hostContextFactory: (host: ts.CompilerHost) => CompilerHostContext) {
|
||||||
const host = ts.createCompilerHost(config.parsed.options, true);
|
const host = ts.createCompilerHost(config.options, true);
|
||||||
|
|
||||||
// HACK: patch the realpath to solve symlink issue here:
|
// HACK: patch the realpath to solve symlink issue here:
|
||||||
// https://github.com/Microsoft/TypeScript/issues/9552
|
// https://github.com/Microsoft/TypeScript/issues/9552
|
||||||
// todo(misko): remove once facade symlinks are removed
|
// todo(misko): remove once facade symlinks are removed
|
||||||
host.realpath = (path) => path;
|
host.realpath = (path) => path;
|
||||||
|
|
||||||
const program = ts.createProgram(config.parsed.fileNames, config.parsed.options, host);
|
const program = ts.createProgram(config.rootNames, config.options, host);
|
||||||
|
|
||||||
return CodeGenerator.create(config.ngOptions, {
|
return CodeGenerator.create(config.options, {
|
||||||
} as any, program, host, hostContextFactory(host)).codegen();
|
} as any, program, host, hostContextFactory(host)).codegen();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
"ng-xi18n": "./src/extract_i18n.js"
|
"ng-xi18n": "./src/extract_i18n.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@angular/tsc-wrapped": "5.0.0-beta.7",
|
|
||||||
"reflect-metadata": "^0.1.2",
|
"reflect-metadata": "^0.1.2",
|
||||||
"minimist": "^1.2.0",
|
"minimist": "^1.2.0",
|
||||||
"tsickle": "^0.24.0",
|
"tsickle": "^0.24.0",
|
||||||
|
@ -431,6 +431,10 @@ function addReferencesToSourceFile(sf: ts.SourceFile, genFileNames: string[]) {
|
|||||||
sf.referencedFiles = newReferencedFiles;
|
sf.referencedFiles = newReferencedFiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getOriginalReferences(sourceFile: ts.SourceFile): ts.FileReference[]|undefined {
|
||||||
|
return sourceFile && (sourceFile as any).originalReferencedFiles;
|
||||||
|
}
|
||||||
|
|
||||||
function dotRelative(from: string, to: string): string {
|
function dotRelative(from: string, to: string): string {
|
||||||
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
const rPath: string = path.relative(from, to).replace(/\\/g, '/');
|
||||||
return rPath.startsWith('.') ? rPath : './' + rPath;
|
return rPath.startsWith('.') ? rPath : './' + rPath;
|
||||||
|
@ -15,7 +15,7 @@ import {TypeCheckHost, translateDiagnostics} from '../diagnostics/translate_diag
|
|||||||
import {ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
import {ModuleMetadata, createBundleIndexHost} from '../metadata/index';
|
||||||
|
|
||||||
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, EmitFlags, Program, SOURCE, TsEmitArguments, TsEmitCallback} from './api';
|
import {CompilerHost, CompilerOptions, CustomTransformers, DEFAULT_ERROR_CODE, Diagnostic, EmitFlags, Program, SOURCE, TsEmitArguments, TsEmitCallback} from './api';
|
||||||
import {TsCompilerAotCompilerTypeCheckHostAdapter} from './compiler_host';
|
import {TsCompilerAotCompilerTypeCheckHostAdapter, getOriginalReferences} from './compiler_host';
|
||||||
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
import {LowerMetadataCache, getExpressionLoweringTransformFactory} from './lower_expressions';
|
||||||
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
import {getAngularEmitterTransformFactory} from './node_emitter_transform';
|
||||||
import {GENERATED_FILES, StructureIsReused, tsStructureIsReused} from './util';
|
import {GENERATED_FILES, StructureIsReused, tsStructureIsReused} from './util';
|
||||||
@ -173,15 +173,34 @@ class AngularCompilerProgram implements Program {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const emitResult = emitCallback({
|
// Restore the original references before we emit so TypeScript doesn't emit
|
||||||
program: this.tsProgram,
|
// a reference to the .d.ts file.
|
||||||
host: this.host,
|
const augmentedReferences = new Map<ts.SourceFile, ts.FileReference[]>();
|
||||||
options: this.options,
|
for (const sourceFile of this.tsProgram.getSourceFiles()) {
|
||||||
writeFile: createWriteFileCallback(genFiles, this.host, outSrcMapping),
|
const originalReferences = getOriginalReferences(sourceFile);
|
||||||
emitOnlyDtsFiles: (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
|
if (originalReferences) {
|
||||||
customTransformers: this.calculateTransforms(genFiles, customTransformers)
|
augmentedReferences.set(sourceFile, sourceFile.referencedFiles);
|
||||||
});
|
sourceFile.referencedFiles = originalReferences;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let emitResult: ts.EmitResult;
|
||||||
|
try {
|
||||||
|
emitResult = emitCallback({
|
||||||
|
program: this.tsProgram,
|
||||||
|
host: this.host,
|
||||||
|
options: this.options,
|
||||||
|
writeFile: createWriteFileCallback(genFiles, this.host, outSrcMapping),
|
||||||
|
emitOnlyDtsFiles: (emitFlags & (EmitFlags.DTS | EmitFlags.JS)) == EmitFlags.DTS,
|
||||||
|
customTransformers: this.calculateTransforms(genFiles, customTransformers)
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
// Restore the references back to the augmented value to ensure that the
|
||||||
|
// checks that TypeScript makes for project structure reuse will succeed.
|
||||||
|
for (const [sourceFile, references] of Array.from(augmentedReferences)) {
|
||||||
|
sourceFile.referencedFiles = references;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!outSrcMapping.length) {
|
if (!outSrcMapping.length) {
|
||||||
// if no files were emitted by TypeScript, also don't emit .json files
|
// if no files were emitted by TypeScript, also don't emit .json files
|
||||||
@ -464,6 +483,7 @@ function getAotCompilerOptions(options: CompilerOptions): AotCompilerOptions {
|
|||||||
enableSummariesForJit: true,
|
enableSummariesForJit: true,
|
||||||
preserveWhitespaces: options.preserveWhitespaces,
|
preserveWhitespaces: options.preserveWhitespaces,
|
||||||
fullTemplateTypeCheck: options.fullTemplateTypeCheck,
|
fullTemplateTypeCheck: options.fullTemplateTypeCheck,
|
||||||
|
rootDir: options.rootDir,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import * as ng from '@angular/compiler-cli';
|
import * as ng from '@angular/compiler-cli';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as ts from 'typescript';
|
import * as ts from 'typescript';
|
||||||
|
|
||||||
@ -247,3 +248,12 @@ const LOWERING_QUICKSTART = {
|
|||||||
export class AppModule { }
|
export class AppModule { }
|
||||||
`
|
`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const tmpdir = process.env.TEST_TMPDIR || os.tmpdir();
|
||||||
|
|
||||||
|
function makeTempDir(): string {
|
||||||
|
const id = (Math.random() * 1000000).toFixed(0);
|
||||||
|
const dir = path.join(tmpdir, `tmp.${id}`);
|
||||||
|
fs.mkdirSync(dir);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/compiler": ["../../dist/packages/compiler"],
|
"@angular/compiler": ["../../dist/packages/compiler"]
|
||||||
"@angular/tsc-wrapped": ["../../dist/packages-dist/tsc-wrapped"]
|
|
||||||
},
|
},
|
||||||
"outDir": "../../dist/packages/compiler-cli"
|
"outDir": "../../dist/packages/compiler-cli"
|
||||||
},
|
},
|
||||||
|
10
packages/compiler-cli/tsconfig-tools.json
Normal file
10
packages/compiler-cli/tsconfig-tools.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig-build.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "../../dist/tools/@angular/compiler-cli",
|
||||||
|
"paths": {
|
||||||
|
"@angular/compiler": ["../../dist/tools/@angular/compiler"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@ import {TypeCheckCompiler} from '../view_compiler/type_check_compiler';
|
|||||||
import {ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
import {ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
|
||||||
|
|
||||||
import {AotCompilerHost} from './compiler_host';
|
import {AotCompilerHost} from './compiler_host';
|
||||||
|
import {AotCompilerOptions} from './compiler_options';
|
||||||
import {GeneratedFile} from './generated_file';
|
import {GeneratedFile} from './generated_file';
|
||||||
import {StaticReflector} from './static_reflector';
|
import {StaticReflector} from './static_reflector';
|
||||||
import {StaticSymbol} from './static_symbol';
|
import {StaticSymbol} from './static_symbol';
|
||||||
@ -45,16 +46,14 @@ export class AotCompiler {
|
|||||||
new Map<StaticSymbol, {template: TemplateAst[], pipes: CompilePipeSummary[]}>();
|
new Map<StaticSymbol, {template: TemplateAst[], pipes: CompilePipeSummary[]}>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private _config: CompilerConfig, private _host: AotCompilerHost,
|
private _config: CompilerConfig, private options: AotCompilerOptions,
|
||||||
private _reflector: StaticReflector, private _metadataResolver: CompileMetadataResolver,
|
private _host: AotCompilerHost, private _reflector: StaticReflector,
|
||||||
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
|
private _metadataResolver: CompileMetadataResolver, private _templateParser: TemplateParser,
|
||||||
private _viewCompiler: ViewCompiler, private _typeCheckCompiler: TypeCheckCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _typeCheckCompiler: TypeCheckCompiler, private _ngModuleCompiler: NgModuleCompiler,
|
||||||
private _summaryResolver: SummaryResolver<StaticSymbol>, private _localeId: string|null,
|
private _outputEmitter: OutputEmitter,
|
||||||
private _translationFormat: string|null,
|
private _summaryResolver: SummaryResolver<StaticSymbol>,
|
||||||
/** TODO(tbosch): remove this flag as it is always on in the new ngc */
|
private _symbolResolver: StaticSymbolResolver) {}
|
||||||
private _enableSummariesForJit: boolean|null, private _symbolResolver: StaticSymbolResolver) {
|
|
||||||
}
|
|
||||||
|
|
||||||
clearCache() { this._metadataResolver.clearCache(); }
|
clearCache() { this._metadataResolver.clearCache(); }
|
||||||
|
|
||||||
@ -121,7 +120,8 @@ export class AotCompiler {
|
|||||||
|
|
||||||
private _createNgFactoryStub(file: NgAnalyzedFile, emitFlags: StubEmitFlags): GeneratedFile[] {
|
private _createNgFactoryStub(file: NgAnalyzedFile, emitFlags: StubEmitFlags): GeneratedFile[] {
|
||||||
const generatedFiles: GeneratedFile[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
const outputCtx = this._createOutputContext(ngfactoryFilePath(file.fileName, true));
|
const outputCtx = this._createOutputContext(
|
||||||
|
calculateGenFileName(ngfactoryFilePath(file.fileName, true), this.options.rootDir));
|
||||||
|
|
||||||
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
file.ngModules.forEach((ngModuleMeta, ngModuleIndex) => {
|
||||||
// Note: the code below needs to executed for StubEmitFlags.Basic and StubEmitFlags.TypeCheck,
|
// Note: the code below needs to executed for StubEmitFlags.Basic and StubEmitFlags.TypeCheck,
|
||||||
@ -209,8 +209,10 @@ export class AotCompiler {
|
|||||||
}
|
}
|
||||||
const encapsulation =
|
const encapsulation =
|
||||||
compMeta.template !.encapsulation || this._config.defaultEncapsulation;
|
compMeta.template !.encapsulation || this._config.defaultEncapsulation;
|
||||||
const outputCtx = this._createOutputContext(_stylesModuleUrl(
|
const outputCtx = this._createOutputContext(calculateGenFileName(
|
||||||
normalizedUrl, encapsulation === ViewEncapsulation.Emulated, fileSuffix));
|
_stylesModuleUrl(
|
||||||
|
normalizedUrl, encapsulation === ViewEncapsulation.Emulated, fileSuffix),
|
||||||
|
this.options.rootDir));
|
||||||
_createEmptyStub(outputCtx);
|
_createEmptyStub(outputCtx);
|
||||||
generatedFiles.push(this._codegenSourceModule(normalizedUrl, outputCtx));
|
generatedFiles.push(this._codegenSourceModule(normalizedUrl, outputCtx));
|
||||||
});
|
});
|
||||||
@ -221,12 +223,13 @@ export class AotCompiler {
|
|||||||
private _createNgSummaryStub(file: NgAnalyzedFile, emitFlags: StubEmitFlags): GeneratedFile[] {
|
private _createNgSummaryStub(file: NgAnalyzedFile, emitFlags: StubEmitFlags): GeneratedFile[] {
|
||||||
const generatedFiles: GeneratedFile[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
// note: .ngsummary.js stubs don't change when we produce type check stubs
|
// note: .ngsummary.js stubs don't change when we produce type check stubs
|
||||||
if (!this._enableSummariesForJit || !(emitFlags & StubEmitFlags.Basic)) {
|
if (!this.options.enableSummariesForJit || !(emitFlags & StubEmitFlags.Basic)) {
|
||||||
return generatedFiles;
|
return generatedFiles;
|
||||||
}
|
}
|
||||||
if (file.directives.length || file.injectables.length || file.ngModules.length ||
|
if (file.directives.length || file.injectables.length || file.ngModules.length ||
|
||||||
file.pipes.length || file.exportsNonSourceFiles) {
|
file.pipes.length || file.exportsNonSourceFiles) {
|
||||||
const outputCtx = this._createOutputContext(summaryForJitFileName(file.fileName, true));
|
const outputCtx = this._createOutputContext(
|
||||||
|
calculateGenFileName(summaryForJitFileName(file.fileName, true), this.options.rootDir));
|
||||||
file.ngModules.forEach(ngModule => {
|
file.ngModules.forEach(ngModule => {
|
||||||
// create exports that user code can reference
|
// create exports that user code can reference
|
||||||
createForJitStub(outputCtx, ngModule.type.reference);
|
createForJitStub(outputCtx, ngModule.type.reference);
|
||||||
@ -295,7 +298,8 @@ export class AotCompiler {
|
|||||||
const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1];
|
const fileSuffix = splitTypescriptSuffix(srcFileUrl, true)[1];
|
||||||
const generatedFiles: GeneratedFile[] = [];
|
const generatedFiles: GeneratedFile[] = [];
|
||||||
|
|
||||||
const outputCtx = this._createOutputContext(ngfactoryFilePath(srcFileUrl, true));
|
const outputCtx = this._createOutputContext(
|
||||||
|
calculateGenFileName(ngfactoryFilePath(srcFileUrl, true), this.options.rootDir));
|
||||||
|
|
||||||
generatedFiles.push(
|
generatedFiles.push(
|
||||||
...this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables, outputCtx));
|
...this._createSummary(srcFileUrl, directives, pipes, ngModules, injectables, outputCtx));
|
||||||
@ -366,7 +370,8 @@ export class AotCompiler {
|
|||||||
metadata: this._metadataResolver.getInjectableSummary(ref) !.type
|
metadata: this._metadataResolver.getInjectableSummary(ref) !.type
|
||||||
}))
|
}))
|
||||||
];
|
];
|
||||||
const forJitOutputCtx = this._createOutputContext(summaryForJitFileName(srcFileName, true));
|
const forJitOutputCtx = this._createOutputContext(
|
||||||
|
calculateGenFileName(summaryForJitFileName(srcFileName, true), this.options.rootDir));
|
||||||
const {json, exportAs} = serializeSummaries(
|
const {json, exportAs} = serializeSummaries(
|
||||||
srcFileName, forJitOutputCtx, this._summaryResolver, this._symbolResolver, symbolSummaries,
|
srcFileName, forJitOutputCtx, this._summaryResolver, this._symbolResolver, symbolSummaries,
|
||||||
typeData);
|
typeData);
|
||||||
@ -377,7 +382,7 @@ export class AotCompiler {
|
|||||||
]));
|
]));
|
||||||
});
|
});
|
||||||
const summaryJson = new GeneratedFile(srcFileName, summaryFileName(srcFileName), json);
|
const summaryJson = new GeneratedFile(srcFileName, summaryFileName(srcFileName), json);
|
||||||
if (this._enableSummariesForJit) {
|
if (this.options.enableSummariesForJit) {
|
||||||
return [summaryJson, this._codegenSourceModule(srcFileName, forJitOutputCtx)];
|
return [summaryJson, this._codegenSourceModule(srcFileName, forJitOutputCtx)];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -387,18 +392,18 @@ export class AotCompiler {
|
|||||||
private _compileModule(outputCtx: OutputContext, ngModule: CompileNgModuleMetadata): void {
|
private _compileModule(outputCtx: OutputContext, ngModule: CompileNgModuleMetadata): void {
|
||||||
const providers: CompileProviderMetadata[] = [];
|
const providers: CompileProviderMetadata[] = [];
|
||||||
|
|
||||||
if (this._localeId) {
|
if (this.options.locale) {
|
||||||
const normalizedLocale = this._localeId.replace(/_/g, '-');
|
const normalizedLocale = this.options.locale.replace(/_/g, '-');
|
||||||
providers.push({
|
providers.push({
|
||||||
token: createTokenForExternalReference(this._reflector, Identifiers.LOCALE_ID),
|
token: createTokenForExternalReference(this._reflector, Identifiers.LOCALE_ID),
|
||||||
useValue: normalizedLocale,
|
useValue: normalizedLocale,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._translationFormat) {
|
if (this.options.i18nFormat) {
|
||||||
providers.push({
|
providers.push({
|
||||||
token: createTokenForExternalReference(this._reflector, Identifiers.TRANSLATIONS_FORMAT),
|
token: createTokenForExternalReference(this._reflector, Identifiers.TRANSLATIONS_FORMAT),
|
||||||
useValue: this._translationFormat
|
useValue: this.options.i18nFormat
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,8 +521,11 @@ export class AotCompiler {
|
|||||||
private _codegenStyles(
|
private _codegenStyles(
|
||||||
srcFileUrl: string, compMeta: CompileDirectiveMetadata,
|
srcFileUrl: string, compMeta: CompileDirectiveMetadata,
|
||||||
stylesheetMetadata: CompileStylesheetMetadata, fileSuffix: string): GeneratedFile {
|
stylesheetMetadata: CompileStylesheetMetadata, fileSuffix: string): GeneratedFile {
|
||||||
const outputCtx = this._createOutputContext(_stylesModuleUrl(
|
const outputCtx = this._createOutputContext(calculateGenFileName(
|
||||||
stylesheetMetadata.moduleUrl !, this._styleCompiler.needsStyleShim(compMeta), fileSuffix));
|
_stylesModuleUrl(
|
||||||
|
stylesheetMetadata.moduleUrl !, this._styleCompiler.needsStyleShim(compMeta),
|
||||||
|
fileSuffix),
|
||||||
|
this.options.rootDir));
|
||||||
const compiledStylesheet =
|
const compiledStylesheet =
|
||||||
this._styleCompiler.compileStyles(outputCtx, compMeta, stylesheetMetadata);
|
this._styleCompiler.compileStyles(outputCtx, compMeta, stylesheetMetadata);
|
||||||
_resolveStyleStatements(
|
_resolveStyleStatements(
|
||||||
@ -723,3 +731,17 @@ export function mergeAnalyzedFiles(analyzedFiles: NgAnalyzedFile[]): NgAnalyzedM
|
|||||||
function mergeAndValidateNgFiles(files: NgAnalyzedFile[]): NgAnalyzedModules {
|
function mergeAndValidateNgFiles(files: NgAnalyzedFile[]): NgAnalyzedModules {
|
||||||
return validateAnalyzedModules(mergeAnalyzedFiles(files));
|
return validateAnalyzedModules(mergeAnalyzedFiles(files));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateGenFileName(fileName: string, rootDir: string | undefined): string {
|
||||||
|
if (!rootDir) return fileName;
|
||||||
|
|
||||||
|
const fileNameParts = fileName.split(/\\|\//);
|
||||||
|
const rootDirParts = rootDir.split(/\\|\//);
|
||||||
|
if (!rootDirParts[rootDirParts.length - 1]) rootDirParts.pop();
|
||||||
|
let i = 0;
|
||||||
|
while (i < Math.min(fileNameParts.length, rootDirParts.length) &&
|
||||||
|
fileNameParts[i] === rootDirParts[i])
|
||||||
|
i++;
|
||||||
|
const result = [...rootDirParts, ...fileNameParts.slice(i)].join('/');
|
||||||
|
return result;
|
||||||
|
}
|
@ -84,9 +84,9 @@ export function createAotCompiler(compilerHost: AotCompilerHost, options: AotCom
|
|||||||
const viewCompiler = new ViewCompiler(staticReflector);
|
const viewCompiler = new ViewCompiler(staticReflector);
|
||||||
const typeCheckCompiler = new TypeCheckCompiler(options, staticReflector);
|
const typeCheckCompiler = new TypeCheckCompiler(options, staticReflector);
|
||||||
const compiler = new AotCompiler(
|
const compiler = new AotCompiler(
|
||||||
config, compilerHost, staticReflector, resolver, tmplParser, new StyleCompiler(urlResolver),
|
config, options, compilerHost, staticReflector, resolver, tmplParser,
|
||||||
viewCompiler, typeCheckCompiler, new NgModuleCompiler(staticReflector),
|
new StyleCompiler(urlResolver), viewCompiler, typeCheckCompiler,
|
||||||
new TypeScriptEmitter(), summaryResolver, options.locale || null, options.i18nFormat || null,
|
new NgModuleCompiler(staticReflector), new TypeScriptEmitter(), summaryResolver,
|
||||||
options.enableSummariesForJit || null, symbolResolver);
|
symbolResolver);
|
||||||
return {compiler, reflector: staticReflector};
|
return {compiler, reflector: staticReflector};
|
||||||
}
|
}
|
||||||
|
@ -17,4 +17,5 @@ export interface AotCompilerOptions {
|
|||||||
enableSummariesForJit?: boolean;
|
enableSummariesForJit?: boolean;
|
||||||
preserveWhitespaces?: boolean;
|
preserveWhitespaces?: boolean;
|
||||||
fullTemplateTypeCheck?: boolean;
|
fullTemplateTypeCheck?: boolean;
|
||||||
|
rootDir?: string;
|
||||||
}
|
}
|
||||||
|
@ -11,4 +11,4 @@
|
|||||||
// replaces this file with production index.ts when it rewrites private symbol
|
// replaces this file with production index.ts when it rewrites private symbol
|
||||||
// names.
|
// names.
|
||||||
|
|
||||||
export * from './public_api';
|
export * from './testing';
|
||||||
|
14
packages/compiler/testing/testing.ts
Normal file
14
packages/compiler/testing/testing.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This file is not used to build this module. It is only used during editing
|
||||||
|
// by the TypeScript language service and during build for verification. `ngc`
|
||||||
|
// replaces this file with production index.ts when it rewrites private symbol
|
||||||
|
// names.
|
||||||
|
|
||||||
|
export * from './public_api';
|
@ -5,19 +5,20 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": "../",
|
"rootDir": "../",
|
||||||
"paths": {
|
"paths": {
|
||||||
"@angular/compiler": ["../../../dist/packages/compiler"]
|
"@angular/compiler": ["../../../dist/packages/compiler"],
|
||||||
|
"@angular/core": ["../../../dist/packages/core"]
|
||||||
},
|
},
|
||||||
"outDir": "../../../dist/packages/compiler"
|
"outDir": "../../../dist/packages/compiler"
|
||||||
},
|
},
|
||||||
|
|
||||||
"files": [
|
"files": [
|
||||||
"public_api.ts",
|
"testing.ts",
|
||||||
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
"../../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||||
],
|
],
|
||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"skipMetadataEmit": true,
|
||||||
"flatModuleId": "@angular/compiler/testing"
|
"skipTemplateCodegen": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,9 @@
|
|||||||
"module": "es2015",
|
"module": "es2015",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"outDir": "../../dist/packages/compiler",
|
"outDir": "../../dist/packages/compiler",
|
||||||
|
"paths": {
|
||||||
|
"@angular/core": ["../../dist/packages/core"]
|
||||||
|
},
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"inlineSources": true,
|
"inlineSources": true,
|
||||||
@ -23,5 +26,11 @@
|
|||||||
"files": [
|
"files": [
|
||||||
"index.ts",
|
"index.ts",
|
||||||
"../../node_modules/zone.js/dist/zone.js.d.ts"
|
"../../node_modules/zone.js/dist/zone.js.d.ts"
|
||||||
]
|
],
|
||||||
|
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"annotateForClosureCompiler": true,
|
||||||
|
"skipMetadataEmit": true,
|
||||||
|
"skipTemplateCodegen": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
9
packages/compiler/tsconfig-tools.json
Normal file
9
packages/compiler/tsconfig-tools.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig-build.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"outDir": "../../dist/tools/@angular/compiler"
|
||||||
|
}
|
||||||
|
}
|
@ -18,7 +18,8 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/core/testing"
|
"flatModuleId": "@angular/core/testing"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
"rxjs/*": ["../../node_modules/rxjs/*"]
|
"rxjs/*": ["../../node_modules/rxjs/*"],
|
||||||
|
"@angular/core": ["."]
|
||||||
},
|
},
|
||||||
"outDir": "../../dist/packages/core"
|
"outDir": "../../dist/packages/core"
|
||||||
},
|
},
|
||||||
@ -18,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "core.js",
|
"flatModuleOutFile": "core.js",
|
||||||
"flatModuleId": "@angular/core"
|
"flatModuleId": "@angular/core"
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "forms.js",
|
"flatModuleOutFile": "forms.js",
|
||||||
"flatModuleId": "@angular/forms"
|
"flatModuleId": "@angular/forms"
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/http/testing"
|
"flatModuleId": "@angular/http/testing"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "http.js",
|
"flatModuleOutFile": "http.js",
|
||||||
"flatModuleId": "@angular/http"
|
"flatModuleId": "@angular/http"
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,7 @@ var rxjsLocation = normalize('../../node_modules/rxjs');
|
|||||||
var tslibLocation = normalize('../../node_modules/tslib');
|
var tslibLocation = normalize('../../node_modules/tslib');
|
||||||
var esm = 'esm/';
|
var esm = 'esm/';
|
||||||
|
|
||||||
var locations = {
|
var locations = {'compiler-cli': normalize('../../dist/packages') + '/'};
|
||||||
'tsc-wrapped': normalize('../../dist/packages-dist') + '/',
|
|
||||||
'compiler-cli': normalize('../../dist/packages') + '/'
|
|
||||||
};
|
|
||||||
|
|
||||||
var esm_suffixes = {};
|
var esm_suffixes = {};
|
||||||
|
|
||||||
@ -29,11 +26,6 @@ function normalize(fileName) {
|
|||||||
|
|
||||||
function resolve(id, from) {
|
function resolve(id, from) {
|
||||||
// console.log('Resolve id:', id, 'from', from)
|
// console.log('Resolve id:', id, 'from', from)
|
||||||
if (id == '@angular/tsc-wrapped') {
|
|
||||||
// Hack to restrict the import to not include the index of @angular/tsc-wrapped so we don't
|
|
||||||
// rollup tsickle.
|
|
||||||
return locations['tsc-wrapped'] + 'tsc-wrapped/src/collector.js';
|
|
||||||
}
|
|
||||||
var match = m.exec(id);
|
var match = m.exec(id);
|
||||||
if (match) {
|
if (match) {
|
||||||
var packageName = match[1];
|
var packageName = match[1];
|
||||||
|
@ -16,9 +16,7 @@
|
|||||||
"@angular/compiler-cli/*": ["../../dist/packages/compiler-cli/*"],
|
"@angular/compiler-cli/*": ["../../dist/packages/compiler-cli/*"],
|
||||||
"@angular/http": ["../../dist/packages/http"],
|
"@angular/http": ["../../dist/packages/http"],
|
||||||
"@angular/platform-server": ["../../dist/packages/platform-server"],
|
"@angular/platform-server": ["../../dist/packages/platform-server"],
|
||||||
"@angular/platform-browser": ["../../dist/packages/platform-browser"],
|
"@angular/platform-browser": ["../../dist/packages/platform-browser"]
|
||||||
"@angular/tsc-wrapped": ["../../dist/packages-dist/tsc-wrapped"],
|
|
||||||
"@angular/tsc-wrapped/*": ["../../dist/packages-dist/tsc-wrapped/*"]
|
|
||||||
},
|
},
|
||||||
"outDir": "../../dist/packages/language-service"
|
"outDir": "../../dist/packages/language-service"
|
||||||
},
|
},
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/platform-browser-dynamic/testing"
|
"flatModuleId": "@angular/platform-browser-dynamic/testing"
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "platform-browser-dynamic.js",
|
"flatModuleOutFile": "platform-browser-dynamic.js",
|
||||||
"flatModuleId": "@angular/platform-browser-dynamic"
|
"flatModuleId": "@angular/platform-browser-dynamic"
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
"rootDir": "../",
|
"rootDir": "../",
|
||||||
"paths": {
|
"paths": {
|
||||||
"rxjs/*": ["../../../node_modules/rxjs/*"],
|
"rxjs/*": ["../../../node_modules/rxjs/*"],
|
||||||
|
"@angular/common": ["../../../dist/packages/common"],
|
||||||
"@angular/core": ["../../../dist/packages/core"],
|
"@angular/core": ["../../../dist/packages/core"],
|
||||||
"@angular/core/testing": ["../../../dist/packages/core/testing"],
|
"@angular/core/testing": ["../../../dist/packages/core/testing"],
|
||||||
"@angular/animations": ["../../../dist/packages/animations"],
|
"@angular/animations": ["../../../dist/packages/animations"],
|
||||||
@ -23,7 +24,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "animations.js",
|
"flatModuleOutFile": "animations.js",
|
||||||
"flatModuleId": "@angular/platform-browser/animations"
|
"flatModuleId": "@angular/platform-browser/animations"
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/platform-browser/testing"
|
"flatModuleId": "@angular/platform-browser/testing"
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "platform-browser.js",
|
"flatModuleOutFile": "platform-browser.js",
|
||||||
"flatModuleId": "@angular/platform-browser"
|
"flatModuleId": "@angular/platform-browser"
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
"@angular/common": "file:../../../dist/packages-dist/common",
|
"@angular/common": "file:../../../dist/packages-dist/common",
|
||||||
"@angular/compiler": "file:../../../dist/packages-dist/compiler",
|
"@angular/compiler": "file:../../../dist/packages-dist/compiler",
|
||||||
"@angular/compiler-cli": "file:../../../dist/packages-dist/compiler-cli",
|
"@angular/compiler-cli": "file:../../../dist/packages-dist/compiler-cli",
|
||||||
"@angular/tsc-wrapped": "file:../../../dist/packages-dist/tsc-wrapped",
|
|
||||||
"@angular/core": "file:../../../dist/packages-dist/core",
|
"@angular/core": "file:../../../dist/packages-dist/core",
|
||||||
"@angular/http": "file:../../../dist/packages-dist/http",
|
"@angular/http": "file:../../../dist/packages-dist/http",
|
||||||
"@angular/platform-browser": "file:../../../dist/packages-dist/platform-browser",
|
"@angular/platform-browser": "file:../../../dist/packages-dist/platform-browser",
|
||||||
|
@ -5,13 +5,16 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": "../",
|
"rootDir": "../",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@angular/animations": ["../../../dist/packages/animations"],
|
||||||
"@angular/animations/browser": ["../../../dist/packages/animations/browser"],
|
"@angular/animations/browser": ["../../../dist/packages/animations/browser"],
|
||||||
"@angular/core": ["../../../dist/packages/core"],
|
"@angular/core": ["../../../dist/packages/core"],
|
||||||
"@angular/core/testing": ["../../../dist/packages/core/testing"],
|
"@angular/core/testing": ["../../../dist/packages/core/testing"],
|
||||||
"@angular/common": ["../../../dist/packages/common"],
|
"@angular/common": ["../../../dist/packages/common"],
|
||||||
|
"@angular/common/http": ["../../../dist/packages/common/http"],
|
||||||
"@angular/common/testing": ["../../../dist/packages/common/testing"],
|
"@angular/common/testing": ["../../../dist/packages/common/testing"],
|
||||||
"@angular/compiler": ["../../../dist/packages/compiler"],
|
"@angular/compiler": ["../../../dist/packages/compiler"],
|
||||||
"@angular/compiler/testing": ["../../../dist/packages/compiler/testing"],
|
"@angular/compiler/testing": ["../../../dist/packages/compiler/testing"],
|
||||||
|
"@angular/http": ["../../../dist/packages/http"],
|
||||||
"@angular/platform-browser": ["../../../dist/packages/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages/platform-browser"],
|
||||||
"@angular/platform-browser/animations": ["../../../dist/packages/platform-browser/animations"],
|
"@angular/platform-browser/animations": ["../../../dist/packages/platform-browser/animations"],
|
||||||
"@angular/platform-browser/testing": ["../../../dist/packages/platform-browser/testing"],
|
"@angular/platform-browser/testing": ["../../../dist/packages/platform-browser/testing"],
|
||||||
@ -31,7 +34,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/platform-server/testing"
|
"flatModuleId": "@angular/platform-server/testing"
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": ".",
|
"rootDir": ".",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@angular/animations": ["../../dist/packages/animations"],
|
||||||
"@angular/animations/browser": ["../../dist/packages/animations/browser"],
|
"@angular/animations/browser": ["../../dist/packages/animations/browser"],
|
||||||
"@angular/core": ["../../dist/packages/core"],
|
"@angular/core": ["../../dist/packages/core"],
|
||||||
"@angular/common": ["../../dist/packages/common"],
|
"@angular/common": ["../../dist/packages/common"],
|
||||||
@ -26,7 +27,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "platform-server.js",
|
"flatModuleOutFile": "platform-server.js",
|
||||||
"flatModuleId": "@angular/platform-server"
|
"flatModuleId": "@angular/platform-server"
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "platform-webworker-dynamic.js",
|
"flatModuleOutFile": "platform-webworker-dynamic.js",
|
||||||
"flatModuleId": "@angular/platform-webworker-dynamic"
|
"flatModuleId": "@angular/platform-webworker-dynamic"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "platform-webworker.js",
|
"flatModuleOutFile": "platform-webworker.js",
|
||||||
"flatModuleId": "@angular/platform-webworker"
|
"flatModuleId": "@angular/platform-webworker"
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "testing.js",
|
"flatModuleOutFile": "testing.js",
|
||||||
"flatModuleId": "@angular/router/testing"
|
"flatModuleId": "@angular/router/testing"
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "router.js",
|
"flatModuleOutFile": "router.js",
|
||||||
"flatModuleId": "@angular/router"
|
"flatModuleId": "@angular/router"
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"rootDir": "../",
|
"rootDir": "../",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"@angular/common": ["../../../dist/packages/common"],
|
||||||
"@angular/core": ["../../../dist/packages/core"],
|
"@angular/core": ["../../../dist/packages/core"],
|
||||||
"@angular/platform-browser": ["../../../dist/packages/platform-browser"],
|
"@angular/platform-browser": ["../../../dist/packages/platform-browser"],
|
||||||
"@angular/router": ["../../../dist/packages/router"],
|
"@angular/router": ["../../../dist/packages/router"],
|
||||||
@ -18,7 +19,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "upgrade.js",
|
"flatModuleOutFile": "upgrade.js",
|
||||||
"flatModuleId": "@angular/router/upgrade"
|
"flatModuleId": "@angular/router/upgrade"
|
||||||
}
|
}
|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
package(default_visibility=["//visibility:public"])
|
|
||||||
load("@build_bazel_rules_typescript//:defs.bzl", "ts_library")
|
|
||||||
|
|
||||||
ts_library(
|
|
||||||
name = "tsc-wrapped",
|
|
||||||
srcs = glob(["**/*.ts"], exclude=[
|
|
||||||
"test/**",
|
|
||||||
]),
|
|
||||||
module_name = "@angular/tsc-wrapped",
|
|
||||||
tsconfig = ":tsconfig-build.json",
|
|
||||||
)
|
|
@ -1,30 +0,0 @@
|
|||||||
# tsc-wrapped
|
|
||||||
|
|
||||||
This package is an internal dependency used by @angular/compiler-cli. Please use that instead.
|
|
||||||
|
|
||||||
This is a wrapper around TypeScript's `tsc` program that allows us to hook in extra extensions.
|
|
||||||
TypeScript will eventually have an extensibility model for arbitrary extensions. We don't want
|
|
||||||
to constrain their design with baggage from a legacy implementation, so this wrapper only
|
|
||||||
supports specific extensions developed by the Angular team:
|
|
||||||
|
|
||||||
- tsickle down-levels Decorators into Annotations so they can be tree-shaken
|
|
||||||
- tsickle can also optionally produce Closure Compiler-friendly code
|
|
||||||
- ./collector.ts emits an extra `.metadata.json` file for every `.d.ts` file written,
|
|
||||||
which retains metadata about decorators that is lost in the TS emit
|
|
||||||
- @angular/compiler-cli extends this library to additionally generate template code
|
|
||||||
|
|
||||||
## TypeScript Decorator metadata collector
|
|
||||||
|
|
||||||
The `.d.ts` format does not preserve information about the Decorators applied to symbols.
|
|
||||||
Some tools, such as Angular template compiler, need access to statically analyzable
|
|
||||||
information about Decorators, so this library allows programs to produce a `foo.metadata.json`
|
|
||||||
to accompany a `foo.d.ts` file, and preserves the information that was lost in the declaration
|
|
||||||
emit.
|
|
||||||
|
|
||||||
## Releasing
|
|
||||||
```
|
|
||||||
$ $(npm bin)/tsc -p tools
|
|
||||||
$ cp tools/tsc-wrapped/package.json dist/tools/@angular/tsc-wrapped/
|
|
||||||
$ npm login [angular]
|
|
||||||
$ npm publish dist/tools/@angular/tsc-wrapped
|
|
||||||
```
|
|
@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
export {main} from './src/main';
|
|
||||||
export * from './index_no_tsickle';
|
|
@ -1,19 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This index allows tsc-wrapped to be used with no dependency on tsickle.
|
|
||||||
// Short-term workaround until tsc-wrapped is removed entirely.
|
|
||||||
export {MetadataWriterHost} from './src/compiler_host';
|
|
||||||
export {CodegenExtension, UserError, createBundleIndexHost} from './src/main_no_tsickle';
|
|
||||||
export {default as AngularCompilerOptions} from './src/options';
|
|
||||||
|
|
||||||
export * from './src/bundler';
|
|
||||||
export * from './src/cli_options';
|
|
||||||
export * from './src/collector';
|
|
||||||
export * from './src/index_writer';
|
|
||||||
export * from './src/schema';
|
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@angular/tsc-wrapped",
|
|
||||||
"version": "0.0.0-PLACEHOLDER",
|
|
||||||
"description": "Wraps the tsc CLI, allowing extensions.",
|
|
||||||
"homepage": "https://github.com/angular/angular/blob/master/packages/tsc-wrapped",
|
|
||||||
"bugs": "https://github.com/angular/angular/issues",
|
|
||||||
"contributors": [
|
|
||||||
"Alex Eagle <alexeagle@google.com>",
|
|
||||||
"Chuck Jazdzewski <chuckj@google.com>"
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"repository": {"type":"git","url":"https://github.com/angular/angular.git"},
|
|
||||||
"dependencies": {
|
|
||||||
"tsickle": "^0.24.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"typescript": "^2.4.2"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,629 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {MetadataCollector} from './collector';
|
|
||||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataMap, MetadataObject, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isInterfaceMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicExpression, isMethodMetadata} from './schema';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// The character set used to produce private names.
|
|
||||||
const PRIVATE_NAME_CHARS = 'abcdefghijklmnopqrstuvwxyz';
|
|
||||||
|
|
||||||
interface Symbol {
|
|
||||||
module: string;
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
// Produced by indirectly by exportAll() for symbols re-export another symbol.
|
|
||||||
exports?: Symbol;
|
|
||||||
|
|
||||||
// Produced by indirectly by exportAll() for symbols are re-exported by another symbol.
|
|
||||||
reexportedAs?: Symbol;
|
|
||||||
|
|
||||||
// Produced by canonicalizeSymbols() for all symbols. A symbol is private if it is not
|
|
||||||
// exported by the index.
|
|
||||||
isPrivate?: boolean;
|
|
||||||
|
|
||||||
// Produced by canonicalizeSymbols() for all symbols. This is the one symbol that
|
|
||||||
// respresents all other symbols and is the only symbol that, among all the re-exported
|
|
||||||
// aliases, whose fields can be trusted to contain the correct information.
|
|
||||||
// For private symbols this is the declaration symbol. For public symbols this is the
|
|
||||||
// symbol that is exported.
|
|
||||||
canonicalSymbol?: Symbol;
|
|
||||||
|
|
||||||
// Produced by canonicalizeSymbols() for all symbols. This the symbol that originally
|
|
||||||
// declared the value and should be used to fetch the value.
|
|
||||||
declaration?: Symbol;
|
|
||||||
|
|
||||||
// A symbol is referenced if it is exported from index or referenced by the value of
|
|
||||||
// a referenced symbol's value.
|
|
||||||
referenced?: boolean;
|
|
||||||
|
|
||||||
// A symbol is marked as a re-export the symbol was rexported from a module that is
|
|
||||||
// not part of the flat module bundle.
|
|
||||||
reexport?: boolean;
|
|
||||||
|
|
||||||
// Only valid for referenced canonical symbols. Produces by convertSymbols().
|
|
||||||
value?: MetadataEntry;
|
|
||||||
|
|
||||||
// Only valid for referenced private symbols. It is the name to use to import the symbol from
|
|
||||||
// the bundle index. Produce by assignPrivateNames();
|
|
||||||
privateName?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BundleEntries { [name: string]: MetadataEntry; }
|
|
||||||
|
|
||||||
export interface BundlePrivateEntry {
|
|
||||||
privateName: string;
|
|
||||||
name: string;
|
|
||||||
module: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface BundledModule {
|
|
||||||
metadata: ModuleMetadata;
|
|
||||||
privates: BundlePrivateEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataBundlerHost {
|
|
||||||
getMetadataFor(moduleName: string): ModuleMetadata|undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
type StaticsMetadata = {
|
|
||||||
[name: string]: MetadataValue | FunctionMetadata;
|
|
||||||
};
|
|
||||||
|
|
||||||
export class MetadataBundler {
|
|
||||||
private symbolMap = new Map<string, Symbol>();
|
|
||||||
private metadataCache = new Map<string, ModuleMetadata|undefined>();
|
|
||||||
private exports = new Map<string, Symbol[]>();
|
|
||||||
private rootModule: string;
|
|
||||||
private exported: Set<Symbol>;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private root: string, private importAs: string|undefined, private host: MetadataBundlerHost) {
|
|
||||||
this.rootModule = `./${path.basename(root)}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getMetadataBundle(): BundledModule {
|
|
||||||
// Export the root module. This also collects the transitive closure of all values referenced by
|
|
||||||
// the exports.
|
|
||||||
const exportedSymbols = this.exportAll(this.rootModule);
|
|
||||||
this.canonicalizeSymbols(exportedSymbols);
|
|
||||||
// TODO: exports? e.g. a module re-exports a symbol from another bundle
|
|
||||||
const metadata = this.getEntries(exportedSymbols);
|
|
||||||
const privates = Array.from(this.symbolMap.values())
|
|
||||||
.filter(s => s.referenced && s.isPrivate)
|
|
||||||
.map(s => ({
|
|
||||||
privateName: s.privateName !,
|
|
||||||
name: s.declaration !.name,
|
|
||||||
module: s.declaration !.module
|
|
||||||
}));
|
|
||||||
const origins = Array.from(this.symbolMap.values())
|
|
||||||
.filter(s => s.referenced && !s.reexport)
|
|
||||||
.reduce<{[name: string]: string}>((p, s) => {
|
|
||||||
p[s.isPrivate ? s.privateName ! : s.name] = s.declaration !.module;
|
|
||||||
return p;
|
|
||||||
}, {});
|
|
||||||
const exports = this.getReExports(exportedSymbols);
|
|
||||||
return {
|
|
||||||
metadata: {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: VERSION,
|
|
||||||
exports: exports.length ? exports : undefined, metadata, origins,
|
|
||||||
importAs: this.importAs !
|
|
||||||
},
|
|
||||||
privates
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static resolveModule(importName: string, from: string): string {
|
|
||||||
return resolveModule(importName, from);
|
|
||||||
}
|
|
||||||
|
|
||||||
private getMetadata(moduleName: string): ModuleMetadata|undefined {
|
|
||||||
let result = this.metadataCache.get(moduleName);
|
|
||||||
if (!result) {
|
|
||||||
if (moduleName.startsWith('.')) {
|
|
||||||
const fullModuleName = resolveModule(moduleName, this.root);
|
|
||||||
result = this.host.getMetadataFor(fullModuleName);
|
|
||||||
}
|
|
||||||
this.metadataCache.set(moduleName, result);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private exportAll(moduleName: string): Symbol[] {
|
|
||||||
const module = this.getMetadata(moduleName);
|
|
||||||
let result = this.exports.get(moduleName);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = [];
|
|
||||||
|
|
||||||
const exportSymbol = (exportedSymbol: Symbol, exportAs: string) => {
|
|
||||||
const symbol = this.symbolOf(moduleName, exportAs);
|
|
||||||
result !.push(symbol);
|
|
||||||
exportedSymbol.reexportedAs = symbol;
|
|
||||||
symbol.exports = exportedSymbol;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export all the symbols defined in this module.
|
|
||||||
if (module && module.metadata) {
|
|
||||||
for (let key in module.metadata) {
|
|
||||||
const data = module.metadata[key];
|
|
||||||
if (isMetadataImportedSymbolReferenceExpression(data)) {
|
|
||||||
// This is a re-export of an imported symbol. Record this as a re-export.
|
|
||||||
const exportFrom = resolveModule(data.module, moduleName);
|
|
||||||
this.exportAll(exportFrom);
|
|
||||||
const symbol = this.symbolOf(exportFrom, data.name);
|
|
||||||
exportSymbol(symbol, key);
|
|
||||||
} else {
|
|
||||||
// Record that this symbol is exported by this module.
|
|
||||||
result.push(this.symbolOf(moduleName, key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export all the re-exports from this module
|
|
||||||
if (module && module.exports) {
|
|
||||||
for (const exportDeclaration of module.exports) {
|
|
||||||
const exportFrom = resolveModule(exportDeclaration.from, moduleName);
|
|
||||||
// Record all the exports from the module even if we don't use it directly.
|
|
||||||
const exportedSymbols = this.exportAll(exportFrom);
|
|
||||||
if (exportDeclaration.export) {
|
|
||||||
// Re-export all the named exports from a module.
|
|
||||||
for (const exportItem of exportDeclaration.export) {
|
|
||||||
const name = typeof exportItem == 'string' ? exportItem : exportItem.name;
|
|
||||||
const exportAs = typeof exportItem == 'string' ? exportItem : exportItem.as;
|
|
||||||
const symbol = this.symbolOf(exportFrom, name);
|
|
||||||
if (exportedSymbols && exportedSymbols.length == 1 && exportedSymbols[0].reexport &&
|
|
||||||
exportedSymbols[0].name == '*') {
|
|
||||||
// This is a named export from a module we have no metadata about. Record the named
|
|
||||||
// export as a re-export.
|
|
||||||
symbol.reexport = true;
|
|
||||||
}
|
|
||||||
exportSymbol(this.symbolOf(exportFrom, name), exportAs);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Re-export all the symbols from the module
|
|
||||||
const exportedSymbols = this.exportAll(exportFrom);
|
|
||||||
for (const exportedSymbol of exportedSymbols) {
|
|
||||||
const name = exportedSymbol.name;
|
|
||||||
exportSymbol(exportedSymbol, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!module) {
|
|
||||||
// If no metadata is found for this import then it is considered external to the
|
|
||||||
// library and should be recorded as a re-export in the final metadata if it is
|
|
||||||
// eventually re-exported.
|
|
||||||
const symbol = this.symbolOf(moduleName, '*');
|
|
||||||
symbol.reexport = true;
|
|
||||||
result.push(symbol);
|
|
||||||
}
|
|
||||||
this.exports.set(moduleName, result);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fill in the canonicalSymbol which is the symbol that should be imported by factories.
|
|
||||||
* The canonical symbol is the one exported by the index file for the bundle or definition
|
|
||||||
* symbol for private symbols that are not exported by bundle index.
|
|
||||||
*/
|
|
||||||
private canonicalizeSymbols(exportedSymbols: Symbol[]) {
|
|
||||||
const symbols = Array.from(this.symbolMap.values());
|
|
||||||
this.exported = new Set(exportedSymbols);
|
|
||||||
symbols.forEach(this.canonicalizeSymbol, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private canonicalizeSymbol(symbol: Symbol) {
|
|
||||||
const rootExport = getRootExport(symbol);
|
|
||||||
const declaration = getSymbolDeclaration(symbol);
|
|
||||||
const isPrivate = !this.exported.has(rootExport);
|
|
||||||
const canonicalSymbol = isPrivate ? declaration : rootExport;
|
|
||||||
symbol.isPrivate = isPrivate;
|
|
||||||
symbol.declaration = declaration;
|
|
||||||
symbol.canonicalSymbol = canonicalSymbol;
|
|
||||||
symbol.reexport = declaration.reexport;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getEntries(exportedSymbols: Symbol[]): BundleEntries {
|
|
||||||
const result: BundleEntries = {};
|
|
||||||
|
|
||||||
const exportedNames = new Set(exportedSymbols.map(s => s.name));
|
|
||||||
let privateName = 0;
|
|
||||||
|
|
||||||
function newPrivateName(): string {
|
|
||||||
while (true) {
|
|
||||||
let digits: string[] = [];
|
|
||||||
let index = privateName++;
|
|
||||||
let base = PRIVATE_NAME_CHARS;
|
|
||||||
while (!digits.length || index > 0) {
|
|
||||||
digits.unshift(base[index % base.length]);
|
|
||||||
index = Math.floor(index / base.length);
|
|
||||||
}
|
|
||||||
digits.unshift('\u0275');
|
|
||||||
const result = digits.join('');
|
|
||||||
if (!exportedNames.has(result)) return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
exportedSymbols.forEach(symbol => this.convertSymbol(symbol));
|
|
||||||
|
|
||||||
const symbolsMap = new Map<string, string[]>();
|
|
||||||
Array.from(this.symbolMap.values()).forEach(symbol => {
|
|
||||||
if (symbol.referenced && !symbol.reexport) {
|
|
||||||
let name = symbol.name;
|
|
||||||
const identifier = `${symbol.declaration!.module}:${symbol.declaration !.name}`;
|
|
||||||
if (symbol.isPrivate && !symbol.privateName) {
|
|
||||||
name = newPrivateName();
|
|
||||||
symbol.privateName = name;
|
|
||||||
}
|
|
||||||
if (symbolsMap.has(identifier)) {
|
|
||||||
const names = symbolsMap.get(identifier);
|
|
||||||
names !.push(name);
|
|
||||||
} else {
|
|
||||||
symbolsMap.set(identifier, [name]);
|
|
||||||
}
|
|
||||||
result[name] = symbol.value !;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// check for duplicated entries
|
|
||||||
symbolsMap.forEach((names: string[], identifier: string) => {
|
|
||||||
if (names.length > 1) {
|
|
||||||
const [module, declaredName] = identifier.split(':');
|
|
||||||
// prefer the export that uses the declared name (if any)
|
|
||||||
let reference = names.indexOf(declaredName);
|
|
||||||
if (reference === -1) {
|
|
||||||
reference = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// keep one entry and replace the others by references
|
|
||||||
names.forEach((name: string, i: number) => {
|
|
||||||
if (i !== reference) {
|
|
||||||
result[name] = {__symbolic: 'reference', name: names[reference]};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getReExports(exportedSymbols: Symbol[]): ModuleExportMetadata[] {
|
|
||||||
type ExportClause = {name: string, as: string}[];
|
|
||||||
const modules = new Map<string, ExportClause>();
|
|
||||||
const exportAlls = new Set<string>();
|
|
||||||
for (const symbol of exportedSymbols) {
|
|
||||||
if (symbol.reexport) {
|
|
||||||
// symbol.declaration is guarenteed to be defined during the phase this method is called.
|
|
||||||
const declaration = symbol.declaration !;
|
|
||||||
const module = declaration.module;
|
|
||||||
if (declaration !.name == '*') {
|
|
||||||
// Reexport all the symbols.
|
|
||||||
exportAlls.add(declaration.module);
|
|
||||||
} else {
|
|
||||||
// Re-export the symbol as the exported name.
|
|
||||||
let entry = modules.get(module);
|
|
||||||
if (!entry) {
|
|
||||||
entry = [];
|
|
||||||
modules.set(module, entry);
|
|
||||||
}
|
|
||||||
const as = symbol.name;
|
|
||||||
const name = declaration.name;
|
|
||||||
entry.push({name, as});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return [
|
|
||||||
...Array.from(exportAlls.values()).map(from => ({from})),
|
|
||||||
...Array.from(modules.entries()).map(([from, exports]) => ({export: exports, from}))
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertSymbol(symbol: Symbol) {
|
|
||||||
// canonicalSymbol is ensured to be defined before this is called.
|
|
||||||
const canonicalSymbol = symbol.canonicalSymbol !;
|
|
||||||
|
|
||||||
if (!canonicalSymbol.referenced) {
|
|
||||||
canonicalSymbol.referenced = true;
|
|
||||||
// declaration is ensured to be definded before this method is called.
|
|
||||||
const declaration = canonicalSymbol.declaration !;
|
|
||||||
const module = this.getMetadata(declaration.module);
|
|
||||||
if (module) {
|
|
||||||
const value = module.metadata[declaration.name];
|
|
||||||
if (value && !declaration.name.startsWith('___')) {
|
|
||||||
canonicalSymbol.value = this.convertEntry(declaration.module, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertEntry(moduleName: string, value: MetadataEntry): MetadataEntry {
|
|
||||||
if (isClassMetadata(value)) {
|
|
||||||
return this.convertClass(moduleName, value);
|
|
||||||
}
|
|
||||||
if (isFunctionMetadata(value)) {
|
|
||||||
return this.convertFunction(moduleName, value);
|
|
||||||
}
|
|
||||||
if (isInterfaceMetadata(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return this.convertValue(moduleName, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertClass(moduleName: string, value: ClassMetadata): ClassMetadata {
|
|
||||||
return {
|
|
||||||
__symbolic: 'class',
|
|
||||||
arity: value.arity,
|
|
||||||
extends: this.convertExpression(moduleName, value.extends) !,
|
|
||||||
decorators:
|
|
||||||
value.decorators && value.decorators.map(d => this.convertExpression(moduleName, d) !),
|
|
||||||
members: this.convertMembers(moduleName, value.members !),
|
|
||||||
statics: value.statics && this.convertStatics(moduleName, value.statics)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertMembers(moduleName: string, members: MetadataMap): MetadataMap {
|
|
||||||
const result: MetadataMap = {};
|
|
||||||
for (const name in members) {
|
|
||||||
const value = members[name];
|
|
||||||
result[name] = value.map(v => this.convertMember(moduleName, v));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertMember(moduleName: string, member: MemberMetadata) {
|
|
||||||
const result: MemberMetadata = {__symbolic: member.__symbolic};
|
|
||||||
result.decorators =
|
|
||||||
member.decorators && member.decorators.map(d => this.convertExpression(moduleName, d) !);
|
|
||||||
if (isMethodMetadata(member)) {
|
|
||||||
(result as MethodMetadata).parameterDecorators = member.parameterDecorators &&
|
|
||||||
member.parameterDecorators.map(
|
|
||||||
d => d && d.map(p => this.convertExpression(moduleName, p) !));
|
|
||||||
if (isConstructorMetadata(member)) {
|
|
||||||
if (member.parameters) {
|
|
||||||
(result as ConstructorMetadata).parameters =
|
|
||||||
member.parameters.map(p => this.convertExpression(moduleName, p));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertStatics(moduleName: string, statics: StaticsMetadata): StaticsMetadata {
|
|
||||||
let result: StaticsMetadata = {};
|
|
||||||
for (const key in statics) {
|
|
||||||
const value = statics[key];
|
|
||||||
result[key] = isFunctionMetadata(value) ? this.convertFunction(moduleName, value) : value;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertFunction(moduleName: string, value: FunctionMetadata): FunctionMetadata {
|
|
||||||
return {
|
|
||||||
__symbolic: 'function',
|
|
||||||
parameters: value.parameters,
|
|
||||||
defaults: value.defaults && value.defaults.map(v => this.convertValue(moduleName, v)),
|
|
||||||
value: this.convertValue(moduleName, value.value)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertValue(moduleName: string, value: MetadataValue): MetadataValue {
|
|
||||||
if (isPrimitive(value)) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
if (isMetadataError(value)) {
|
|
||||||
return this.convertError(moduleName, value);
|
|
||||||
}
|
|
||||||
if (isMetadataSymbolicExpression(value)) {
|
|
||||||
return this.convertExpression(moduleName, value) !;
|
|
||||||
}
|
|
||||||
if (Array.isArray(value)) {
|
|
||||||
return value.map(v => this.convertValue(moduleName, v));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise it is a metadata object.
|
|
||||||
const object = value as MetadataObject;
|
|
||||||
const result: MetadataObject = {};
|
|
||||||
for (const key in object) {
|
|
||||||
result[key] = this.convertValue(moduleName, object[key]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertExpression(
|
|
||||||
moduleName: string, value: MetadataSymbolicExpression|MetadataError|null|
|
|
||||||
undefined): MetadataSymbolicExpression|MetadataError|undefined|null {
|
|
||||||
if (value) {
|
|
||||||
switch (value.__symbolic) {
|
|
||||||
case 'error':
|
|
||||||
return this.convertError(moduleName, value as MetadataError);
|
|
||||||
case 'reference':
|
|
||||||
return this.convertReference(moduleName, value as MetadataSymbolicReferenceExpression);
|
|
||||||
default:
|
|
||||||
return this.convertExpressionNode(moduleName, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertError(module: string, value: MetadataError): MetadataError {
|
|
||||||
return {
|
|
||||||
__symbolic: 'error',
|
|
||||||
message: value.message,
|
|
||||||
line: value.line,
|
|
||||||
character: value.character,
|
|
||||||
context: value.context, module
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertReference(moduleName: string, value: MetadataSymbolicReferenceExpression):
|
|
||||||
MetadataSymbolicReferenceExpression|MetadataError|undefined {
|
|
||||||
const createReference = (symbol: Symbol): MetadataSymbolicReferenceExpression => {
|
|
||||||
const declaration = symbol.declaration !;
|
|
||||||
if (declaration.module.startsWith('.')) {
|
|
||||||
// Reference to a symbol defined in the module. Ensure it is converted then return a
|
|
||||||
// references to the final symbol.
|
|
||||||
this.convertSymbol(symbol);
|
|
||||||
return {
|
|
||||||
__symbolic: 'reference',
|
|
||||||
get name() {
|
|
||||||
// Resolved lazily because private names are assigned late.
|
|
||||||
const canonicalSymbol = symbol.canonicalSymbol !;
|
|
||||||
if (canonicalSymbol.isPrivate == null) {
|
|
||||||
throw Error('Invalid state: isPrivate was not initialized');
|
|
||||||
}
|
|
||||||
return canonicalSymbol.isPrivate ? canonicalSymbol.privateName ! : canonicalSymbol.name;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
// The symbol was a re-exported symbol from another module. Return a reference to the
|
|
||||||
// original imported symbol.
|
|
||||||
return {__symbolic: 'reference', name: declaration.name, module: declaration.module};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isMetadataGlobalReferenceExpression(value)) {
|
|
||||||
const metadata = this.getMetadata(moduleName);
|
|
||||||
if (metadata && metadata.metadata && metadata.metadata[value.name]) {
|
|
||||||
// Reference to a symbol defined in the module
|
|
||||||
return createReference(this.canonicalSymbolOf(moduleName, value.name));
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a reference has arguments, the arguments need to be converted.
|
|
||||||
if (value.arguments) {
|
|
||||||
return {
|
|
||||||
__symbolic: 'reference',
|
|
||||||
name: value.name,
|
|
||||||
arguments: value.arguments.map(a => this.convertValue(moduleName, a))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global references without arguments (such as to Math or JSON) are unmodified.
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMetadataImportedSymbolReferenceExpression(value)) {
|
|
||||||
// References to imported symbols are separated into two, references to bundled modules and
|
|
||||||
// references to modules external to the bundle. If the module reference is relative it is
|
|
||||||
// assumed to be in the bundle. If it is Global it is assumed to be outside the bundle.
|
|
||||||
// References to symbols outside the bundle are left unmodified. References to symbol inside
|
|
||||||
// the bundle need to be converted to a bundle import reference reachable from the bundle
|
|
||||||
// index.
|
|
||||||
|
|
||||||
if (value.module.startsWith('.')) {
|
|
||||||
// Reference is to a symbol defined inside the module. Convert the reference to a reference
|
|
||||||
// to the canonical symbol.
|
|
||||||
const referencedModule = resolveModule(value.module, moduleName);
|
|
||||||
const referencedName = value.name;
|
|
||||||
return createReference(this.canonicalSymbolOf(referencedModule, referencedName));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value is a reference to a symbol defined outside the module.
|
|
||||||
if (value.arguments) {
|
|
||||||
// If a reference has arguments the arguments need to be converted.
|
|
||||||
return {
|
|
||||||
__symbolic: 'reference',
|
|
||||||
name: value.name,
|
|
||||||
module: value.module,
|
|
||||||
arguments: value.arguments.map(a => this.convertValue(moduleName, a))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isMetadataModuleReferenceExpression(value)) {
|
|
||||||
// Cannot support references to bundled modules as the internal modules of a bundle are erased
|
|
||||||
// by the bundler.
|
|
||||||
if (value.module.startsWith('.')) {
|
|
||||||
return {
|
|
||||||
__symbolic: 'error',
|
|
||||||
message: 'Unsupported bundled module reference',
|
|
||||||
context: {module: value.module}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// References to unbundled modules are unmodified.
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private convertExpressionNode(moduleName: string, value: MetadataSymbolicExpression):
|
|
||||||
MetadataSymbolicExpression {
|
|
||||||
const result: MetadataSymbolicExpression = {__symbolic: value.__symbolic};
|
|
||||||
for (const key in value) {
|
|
||||||
(result as any)[key] = this.convertValue(moduleName, (value as any)[key]);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private symbolOf(module: string, name: string): Symbol {
|
|
||||||
const symbolKey = `${module}:${name}`;
|
|
||||||
let symbol = this.symbolMap.get(symbolKey);
|
|
||||||
if (!symbol) {
|
|
||||||
symbol = {module, name};
|
|
||||||
this.symbolMap.set(symbolKey, symbol);
|
|
||||||
}
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
private canonicalSymbolOf(module: string, name: string): Symbol {
|
|
||||||
// Ensure the module has been seen.
|
|
||||||
this.exportAll(module);
|
|
||||||
const symbol = this.symbolOf(module, name);
|
|
||||||
if (!symbol.canonicalSymbol) {
|
|
||||||
this.canonicalizeSymbol(symbol);
|
|
||||||
}
|
|
||||||
return symbol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CompilerHostAdapter implements MetadataBundlerHost {
|
|
||||||
private collector = new MetadataCollector();
|
|
||||||
|
|
||||||
constructor(private host: ts.CompilerHost) {}
|
|
||||||
|
|
||||||
getMetadataFor(fileName: string): ModuleMetadata|undefined {
|
|
||||||
const sourceFile = this.host.getSourceFile(fileName + '.ts', ts.ScriptTarget.Latest);
|
|
||||||
return this.collector.getMetadata(sourceFile);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveModule(importName: string, from: string): string {
|
|
||||||
if (importName.startsWith('.') && from) {
|
|
||||||
let normalPath = path.normalize(path.join(path.dirname(from), importName));
|
|
||||||
if (!normalPath.startsWith('.') && from.startsWith('.')) {
|
|
||||||
// path.normalize() preserves leading '../' but not './'. This adds it back.
|
|
||||||
normalPath = `.${path.sep}${normalPath}`;
|
|
||||||
}
|
|
||||||
// Replace windows path delimiters with forward-slashes. Otherwise the paths are not
|
|
||||||
// TypeScript compatible when building the bundle.
|
|
||||||
return normalPath.replace(/\\/g, '/');
|
|
||||||
}
|
|
||||||
return importName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPrimitive(o: any): o is boolean|string|number {
|
|
||||||
return o === null || (typeof o !== 'function' && typeof o !== 'object');
|
|
||||||
}
|
|
||||||
|
|
||||||
function getRootExport(symbol: Symbol): Symbol {
|
|
||||||
return symbol.reexportedAs ? getRootExport(symbol.reexportedAs) : symbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSymbolDeclaration(symbol: Symbol): Symbol {
|
|
||||||
return symbol.exports ? getSymbolDeclaration(symbol.exports) : symbol;
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
export class CliOptions {
|
|
||||||
public basePath: string|null;
|
|
||||||
constructor({basePath = null}: {basePath?: string | null}) { this.basePath = basePath; }
|
|
||||||
}
|
|
||||||
|
|
||||||
export class I18nExtractionCliOptions extends CliOptions {
|
|
||||||
i18nFormat: string|null;
|
|
||||||
locale: string|null;
|
|
||||||
outFile: string|null;
|
|
||||||
|
|
||||||
constructor({i18nFormat = null, locale = null, outFile = null}: {
|
|
||||||
i18nFormat?: string,
|
|
||||||
locale?: string,
|
|
||||||
outFile?: string,
|
|
||||||
}) {
|
|
||||||
super({});
|
|
||||||
this.i18nFormat = i18nFormat;
|
|
||||||
this.locale = locale;
|
|
||||||
this.outFile = outFile;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class NgcCliOptions extends CliOptions {
|
|
||||||
public i18nFormat: string|null;
|
|
||||||
public i18nFile: string|null;
|
|
||||||
public locale: string|null;
|
|
||||||
public missingTranslation: string|null;
|
|
||||||
|
|
||||||
constructor({i18nFormat = null, i18nFile = null, locale = null, missingTranslation = null,
|
|
||||||
basePath = null}: {
|
|
||||||
i18nFormat?: string | null,
|
|
||||||
i18nFile?: string|null,
|
|
||||||
locale?: string|null,
|
|
||||||
missingTranslation?: string|null,
|
|
||||||
basePath?: string|null
|
|
||||||
}) {
|
|
||||||
super({basePath: basePath});
|
|
||||||
this.i18nFormat = i18nFormat;
|
|
||||||
this.i18nFile = i18nFile;
|
|
||||||
this.locale = locale;
|
|
||||||
this.missingTranslation = missingTranslation;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,774 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {Evaluator, errorSymbol} from './evaluator';
|
|
||||||
import {ClassMetadata, ConstructorMetadata, FunctionMetadata, InterfaceMetadata, MemberMetadata, MetadataEntry, MetadataError, MetadataMap, MetadataSymbolicBinaryExpression, MetadataSymbolicCallExpression, MetadataSymbolicExpression, MetadataSymbolicIfExpression, MetadataSymbolicIndexExpression, MetadataSymbolicPrefixExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataSymbolicSpreadExpression, MetadataValue, MethodMetadata, ModuleExportMetadata, ModuleMetadata, VERSION, isClassMetadata, isConstructorMetadata, isFunctionMetadata, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataSymbolicExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression, isMethodMetadata} from './schema';
|
|
||||||
import {Symbols} from './symbols';
|
|
||||||
|
|
||||||
// In TypeScript 2.1 these flags moved
|
|
||||||
// These helpers work for both 2.0 and 2.1.
|
|
||||||
const isExport = (ts as any).ModifierFlags ?
|
|
||||||
((node: ts.Node) =>
|
|
||||||
!!((ts as any).getCombinedModifierFlags(node) & (ts as any).ModifierFlags.Export)) :
|
|
||||||
((node: ts.Node) => !!((node.flags & (ts as any).NodeFlags.Export)));
|
|
||||||
const isStatic = (ts as any).ModifierFlags ?
|
|
||||||
((node: ts.Node) =>
|
|
||||||
!!((ts as any).getCombinedModifierFlags(node) & (ts as any).ModifierFlags.Static)) :
|
|
||||||
((node: ts.Node) => !!((node.flags & (ts as any).NodeFlags.Static)));
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A set of collector options to use when collecting metadata.
|
|
||||||
*/
|
|
||||||
export interface CollectorOptions {
|
|
||||||
/**
|
|
||||||
* Version of the metadata to collect.
|
|
||||||
*/
|
|
||||||
version?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect a hidden field "$quoted$" in objects literals that record when the key was quoted in
|
|
||||||
* the source.
|
|
||||||
*/
|
|
||||||
quotedNames?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not simplify invalid expressions.
|
|
||||||
*/
|
|
||||||
verboseInvalidExpression?: boolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An expression substitution callback.
|
|
||||||
*/
|
|
||||||
substituteExpression?: (value: MetadataValue, node: ts.Node) => MetadataValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Collect decorator metadata from a TypeScript module.
|
|
||||||
*/
|
|
||||||
export class MetadataCollector {
|
|
||||||
constructor(private options: CollectorOptions = {}) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a JSON.stringify friendly form describing the decorators of the exported classes from
|
|
||||||
* the source file that is expected to correspond to a module.
|
|
||||||
*/
|
|
||||||
public getMetadata(
|
|
||||||
sourceFile: ts.SourceFile, strict: boolean = false,
|
|
||||||
substituteExpression?: (value: MetadataValue, node: ts.Node) => MetadataValue): ModuleMetadata
|
|
||||||
|undefined {
|
|
||||||
const locals = new Symbols(sourceFile);
|
|
||||||
const nodeMap =
|
|
||||||
new Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>();
|
|
||||||
const composedSubstituter = substituteExpression && this.options.substituteExpression ?
|
|
||||||
(value: MetadataValue, node: ts.Node) =>
|
|
||||||
this.options.substituteExpression !(substituteExpression(value, node), node) :
|
|
||||||
substituteExpression;
|
|
||||||
const evaluatorOptions = substituteExpression ?
|
|
||||||
{...this.options, substituteExpression: composedSubstituter} :
|
|
||||||
this.options;
|
|
||||||
let metadata: {[name: string]: MetadataValue | ClassMetadata | FunctionMetadata}|undefined;
|
|
||||||
const evaluator = new Evaluator(locals, nodeMap, evaluatorOptions, (name, value) => {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] = value;
|
|
||||||
});
|
|
||||||
let exports: ModuleExportMetadata[]|undefined = undefined;
|
|
||||||
|
|
||||||
function objFromDecorator(decoratorNode: ts.Decorator): MetadataSymbolicExpression {
|
|
||||||
return <MetadataSymbolicExpression>evaluator.evaluateNode(decoratorNode.expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
function recordEntry<T extends MetadataEntry>(entry: T, node: ts.Node): T {
|
|
||||||
nodeMap.set(entry, node);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
function errorSym(
|
|
||||||
message: string, node?: ts.Node, context?: {[name: string]: string}): MetadataError {
|
|
||||||
return errorSymbol(message, node, context, sourceFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
function maybeGetSimpleFunction(
|
|
||||||
functionDeclaration: ts.FunctionDeclaration |
|
|
||||||
ts.MethodDeclaration): {func: FunctionMetadata, name: string}|undefined {
|
|
||||||
if (functionDeclaration.name && functionDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const nameNode = <ts.Identifier>functionDeclaration.name;
|
|
||||||
const functionName = nameNode.text;
|
|
||||||
const functionBody = functionDeclaration.body;
|
|
||||||
if (functionBody && functionBody.statements.length == 1) {
|
|
||||||
const statement = functionBody.statements[0];
|
|
||||||
if (statement.kind === ts.SyntaxKind.ReturnStatement) {
|
|
||||||
const returnStatement = <ts.ReturnStatement>statement;
|
|
||||||
if (returnStatement.expression) {
|
|
||||||
const func: FunctionMetadata = {
|
|
||||||
__symbolic: 'function',
|
|
||||||
parameters: namesOf(functionDeclaration.parameters),
|
|
||||||
value: evaluator.evaluateNode(returnStatement.expression)
|
|
||||||
};
|
|
||||||
if (functionDeclaration.parameters.some(p => p.initializer != null)) {
|
|
||||||
func.defaults = functionDeclaration.parameters.map(
|
|
||||||
p => p.initializer && evaluator.evaluateNode(p.initializer));
|
|
||||||
}
|
|
||||||
return recordEntry({func, name: functionName}, functionDeclaration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function classMetadataOf(classDeclaration: ts.ClassDeclaration): ClassMetadata {
|
|
||||||
const result: ClassMetadata = {__symbolic: 'class'};
|
|
||||||
|
|
||||||
function getDecorators(decorators: ts.Decorator[] | undefined): MetadataSymbolicExpression[]|
|
|
||||||
undefined {
|
|
||||||
if (decorators && decorators.length)
|
|
||||||
return decorators.map(decorator => objFromDecorator(decorator));
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
function referenceFrom(node: ts.Node): MetadataSymbolicReferenceExpression|MetadataError|
|
|
||||||
MetadataSymbolicSelectExpression {
|
|
||||||
const result = evaluator.evaluateNode(node);
|
|
||||||
if (isMetadataError(result) || isMetadataSymbolicReferenceExpression(result) ||
|
|
||||||
isMetadataSymbolicSelectExpression(result)) {
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return errorSym('Symbol reference expected', node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add class parents
|
|
||||||
if (classDeclaration.heritageClauses) {
|
|
||||||
classDeclaration.heritageClauses.forEach((hc) => {
|
|
||||||
if (hc.token === ts.SyntaxKind.ExtendsKeyword && hc.types) {
|
|
||||||
hc.types.forEach(type => result.extends = referenceFrom(type.expression));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add arity if the type is generic
|
|
||||||
const typeParameters = classDeclaration.typeParameters;
|
|
||||||
if (typeParameters && typeParameters.length) {
|
|
||||||
result.arity = typeParameters.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add class decorators
|
|
||||||
if (classDeclaration.decorators) {
|
|
||||||
result.decorators = getDecorators(classDeclaration.decorators);
|
|
||||||
}
|
|
||||||
|
|
||||||
// member decorators
|
|
||||||
let members: MetadataMap|null = null;
|
|
||||||
function recordMember(name: string, metadata: MemberMetadata) {
|
|
||||||
if (!members) members = {};
|
|
||||||
const data = members.hasOwnProperty(name) ? members[name] : [];
|
|
||||||
data.push(metadata);
|
|
||||||
members[name] = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// static member
|
|
||||||
let statics: {[name: string]: MetadataValue | FunctionMetadata}|null = null;
|
|
||||||
function recordStaticMember(name: string, value: MetadataValue | FunctionMetadata) {
|
|
||||||
if (!statics) statics = {};
|
|
||||||
statics[name] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const member of classDeclaration.members) {
|
|
||||||
let isConstructor = false;
|
|
||||||
switch (member.kind) {
|
|
||||||
case ts.SyntaxKind.Constructor:
|
|
||||||
case ts.SyntaxKind.MethodDeclaration:
|
|
||||||
isConstructor = member.kind === ts.SyntaxKind.Constructor;
|
|
||||||
const method = <ts.MethodDeclaration|ts.ConstructorDeclaration>member;
|
|
||||||
if (isStatic(method)) {
|
|
||||||
const maybeFunc = maybeGetSimpleFunction(<ts.MethodDeclaration>method);
|
|
||||||
if (maybeFunc) {
|
|
||||||
recordStaticMember(maybeFunc.name, maybeFunc.func);
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const methodDecorators = getDecorators(method.decorators);
|
|
||||||
const parameters = method.parameters;
|
|
||||||
const parameterDecoratorData:
|
|
||||||
((MetadataSymbolicExpression | MetadataError)[] | undefined)[] = [];
|
|
||||||
const parametersData:
|
|
||||||
(MetadataSymbolicReferenceExpression | MetadataError |
|
|
||||||
MetadataSymbolicSelectExpression | null)[] = [];
|
|
||||||
let hasDecoratorData: boolean = false;
|
|
||||||
let hasParameterData: boolean = false;
|
|
||||||
for (const parameter of parameters) {
|
|
||||||
const parameterData = getDecorators(parameter.decorators);
|
|
||||||
parameterDecoratorData.push(parameterData);
|
|
||||||
hasDecoratorData = hasDecoratorData || !!parameterData;
|
|
||||||
if (isConstructor) {
|
|
||||||
if (parameter.type) {
|
|
||||||
parametersData.push(referenceFrom(parameter.type));
|
|
||||||
} else {
|
|
||||||
parametersData.push(null);
|
|
||||||
}
|
|
||||||
hasParameterData = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const data: MethodMetadata = {__symbolic: isConstructor ? 'constructor' : 'method'};
|
|
||||||
const name = isConstructor ? '__ctor__' : evaluator.nameOf(member.name);
|
|
||||||
if (methodDecorators) {
|
|
||||||
data.decorators = methodDecorators;
|
|
||||||
}
|
|
||||||
if (hasDecoratorData) {
|
|
||||||
data.parameterDecorators = parameterDecoratorData;
|
|
||||||
}
|
|
||||||
if (hasParameterData) {
|
|
||||||
(<ConstructorMetadata>data).parameters = parametersData;
|
|
||||||
}
|
|
||||||
if (!isMetadataError(name)) {
|
|
||||||
recordMember(name, data);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.PropertyDeclaration:
|
|
||||||
case ts.SyntaxKind.GetAccessor:
|
|
||||||
case ts.SyntaxKind.SetAccessor:
|
|
||||||
const property = <ts.PropertyDeclaration>member;
|
|
||||||
if (isStatic(property)) {
|
|
||||||
const name = evaluator.nameOf(property.name);
|
|
||||||
if (!isMetadataError(name)) {
|
|
||||||
if (property.initializer) {
|
|
||||||
const value = evaluator.evaluateNode(property.initializer);
|
|
||||||
recordStaticMember(name, value);
|
|
||||||
} else {
|
|
||||||
recordStaticMember(name, errorSym('Variable not initialized', property.name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const propertyDecorators = getDecorators(property.decorators);
|
|
||||||
if (propertyDecorators) {
|
|
||||||
const name = evaluator.nameOf(property.name);
|
|
||||||
if (!isMetadataError(name)) {
|
|
||||||
recordMember(name, {__symbolic: 'property', decorators: propertyDecorators});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (members) {
|
|
||||||
result.members = members;
|
|
||||||
}
|
|
||||||
if (statics) {
|
|
||||||
result.statics = statics;
|
|
||||||
}
|
|
||||||
|
|
||||||
return recordEntry(result, classDeclaration);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect all exported symbols from an exports clause.
|
|
||||||
const exportMap = new Map<string, string>();
|
|
||||||
ts.forEachChild(sourceFile, node => {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ExportDeclaration:
|
|
||||||
const exportDeclaration = <ts.ExportDeclaration>node;
|
|
||||||
const {moduleSpecifier, exportClause} = exportDeclaration;
|
|
||||||
|
|
||||||
if (!moduleSpecifier) {
|
|
||||||
// If there is a module specifier there is also an exportClause
|
|
||||||
exportClause !.elements.forEach(spec => {
|
|
||||||
const exportedAs = spec.name.text;
|
|
||||||
const name = (spec.propertyName || spec.name).text;
|
|
||||||
exportMap.set(name, exportedAs);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const isExportedIdentifier = (identifier?: ts.Identifier) =>
|
|
||||||
identifier && exportMap.has(identifier.text);
|
|
||||||
const isExported =
|
|
||||||
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.TypeAliasDeclaration |
|
|
||||||
ts.InterfaceDeclaration | ts.EnumDeclaration) =>
|
|
||||||
isExport(node) || isExportedIdentifier(node.name);
|
|
||||||
const exportedIdentifierName = (identifier?: ts.Identifier) =>
|
|
||||||
identifier && (exportMap.get(identifier.text) || identifier.text);
|
|
||||||
const exportedName =
|
|
||||||
(node: ts.FunctionDeclaration | ts.ClassDeclaration | ts.InterfaceDeclaration |
|
|
||||||
ts.TypeAliasDeclaration | ts.EnumDeclaration) => exportedIdentifierName(node.name);
|
|
||||||
|
|
||||||
|
|
||||||
// Predeclare classes and functions
|
|
||||||
ts.forEachChild(sourceFile, node => {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ClassDeclaration:
|
|
||||||
const classDeclaration = <ts.ClassDeclaration>node;
|
|
||||||
if (classDeclaration.name) {
|
|
||||||
const className = classDeclaration.name.text;
|
|
||||||
if (isExported(classDeclaration)) {
|
|
||||||
locals.define(
|
|
||||||
className, {__symbolic: 'reference', name: exportedName(classDeclaration)});
|
|
||||||
} else {
|
|
||||||
locals.define(
|
|
||||||
className, errorSym('Reference to non-exported class', node, {className}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.InterfaceDeclaration:
|
|
||||||
const interfaceDeclaration = <ts.InterfaceDeclaration>node;
|
|
||||||
if (interfaceDeclaration.name) {
|
|
||||||
const interfaceName = interfaceDeclaration.name.text;
|
|
||||||
// All references to interfaces should be converted to references to `any`.
|
|
||||||
locals.define(interfaceName, {__symbolic: 'reference', name: 'any'});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.FunctionDeclaration:
|
|
||||||
const functionDeclaration = <ts.FunctionDeclaration>node;
|
|
||||||
if (!isExported(functionDeclaration)) {
|
|
||||||
// Report references to this function as an error.
|
|
||||||
const nameNode = functionDeclaration.name;
|
|
||||||
if (nameNode && nameNode.text) {
|
|
||||||
locals.define(
|
|
||||||
nameNode.text,
|
|
||||||
errorSym(
|
|
||||||
'Reference to a non-exported function', nameNode, {name: nameNode.text}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
ts.forEachChild(sourceFile, node => {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ExportDeclaration:
|
|
||||||
// Record export declarations
|
|
||||||
const exportDeclaration = <ts.ExportDeclaration>node;
|
|
||||||
const {moduleSpecifier, exportClause} = exportDeclaration;
|
|
||||||
|
|
||||||
if (!moduleSpecifier) {
|
|
||||||
// no module specifier -> export {propName as name};
|
|
||||||
if (exportClause) {
|
|
||||||
exportClause.elements.forEach(spec => {
|
|
||||||
const name = spec.name.text;
|
|
||||||
// If the symbol was not already exported, export a reference since it is a
|
|
||||||
// reference to an import
|
|
||||||
if (!metadata || !metadata[name]) {
|
|
||||||
const propNode = spec.propertyName || spec.name;
|
|
||||||
const value: MetadataValue = evaluator.evaluateNode(propNode);
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] = recordEntry(value, node);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (moduleSpecifier && moduleSpecifier.kind == ts.SyntaxKind.StringLiteral) {
|
|
||||||
// Ignore exports that don't have string literals as exports.
|
|
||||||
// This is allowed by the syntax but will be flagged as an error by the type checker.
|
|
||||||
const from = (<ts.StringLiteral>moduleSpecifier).text;
|
|
||||||
const moduleExport: ModuleExportMetadata = {from};
|
|
||||||
if (exportClause) {
|
|
||||||
moduleExport.export = exportClause.elements.map(
|
|
||||||
spec => spec.propertyName ? {name: spec.propertyName.text, as: spec.name.text} :
|
|
||||||
spec.name.text);
|
|
||||||
}
|
|
||||||
if (!exports) exports = [];
|
|
||||||
exports.push(moduleExport);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.ClassDeclaration:
|
|
||||||
const classDeclaration = <ts.ClassDeclaration>node;
|
|
||||||
if (classDeclaration.name) {
|
|
||||||
if (isExported(classDeclaration)) {
|
|
||||||
const name = exportedName(classDeclaration);
|
|
||||||
if (name) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] = classMetadataOf(classDeclaration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise don't record metadata for the class.
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.TypeAliasDeclaration:
|
|
||||||
const typeDeclaration = <ts.TypeAliasDeclaration>node;
|
|
||||||
if (typeDeclaration.name && isExported(typeDeclaration)) {
|
|
||||||
const name = exportedName(typeDeclaration);
|
|
||||||
if (name) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] = {__symbolic: 'interface'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.InterfaceDeclaration:
|
|
||||||
const interfaceDeclaration = <ts.InterfaceDeclaration>node;
|
|
||||||
if (interfaceDeclaration.name && isExported(interfaceDeclaration)) {
|
|
||||||
const name = exportedName(interfaceDeclaration);
|
|
||||||
if (name) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] = {__symbolic: 'interface'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.FunctionDeclaration:
|
|
||||||
// Record functions that return a single value. Record the parameter
|
|
||||||
// names substitution will be performed by the StaticReflector.
|
|
||||||
const functionDeclaration = <ts.FunctionDeclaration>node;
|
|
||||||
if (isExported(functionDeclaration) && functionDeclaration.name) {
|
|
||||||
const name = exportedName(functionDeclaration);
|
|
||||||
const maybeFunc = maybeGetSimpleFunction(functionDeclaration);
|
|
||||||
if (name) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] =
|
|
||||||
maybeFunc ? recordEntry(maybeFunc.func, node) : {__symbolic: 'function'};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.EnumDeclaration:
|
|
||||||
const enumDeclaration = <ts.EnumDeclaration>node;
|
|
||||||
if (isExported(enumDeclaration)) {
|
|
||||||
const enumValueHolder: {[name: string]: MetadataValue} = {};
|
|
||||||
const enumName = exportedName(enumDeclaration);
|
|
||||||
let nextDefaultValue: MetadataValue = 0;
|
|
||||||
let writtenMembers = 0;
|
|
||||||
for (const member of enumDeclaration.members) {
|
|
||||||
let enumValue: MetadataValue;
|
|
||||||
if (!member.initializer) {
|
|
||||||
enumValue = nextDefaultValue;
|
|
||||||
} else {
|
|
||||||
enumValue = evaluator.evaluateNode(member.initializer);
|
|
||||||
}
|
|
||||||
let name: string|undefined = undefined;
|
|
||||||
if (member.name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const identifier = <ts.Identifier>member.name;
|
|
||||||
name = identifier.text;
|
|
||||||
enumValueHolder[name] = enumValue;
|
|
||||||
writtenMembers++;
|
|
||||||
}
|
|
||||||
if (typeof enumValue === 'number') {
|
|
||||||
nextDefaultValue = enumValue + 1;
|
|
||||||
} else if (name) {
|
|
||||||
nextDefaultValue = {
|
|
||||||
__symbolic: 'binary',
|
|
||||||
operator: '+',
|
|
||||||
left: {
|
|
||||||
__symbolic: 'select',
|
|
||||||
expression: recordEntry({__symbolic: 'reference', name: enumName}, node), name
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
nextDefaultValue =
|
|
||||||
recordEntry(errorSym('Unsuppported enum member name', member.name), node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (writtenMembers) {
|
|
||||||
if (enumName) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[enumName] = recordEntry(enumValueHolder, node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ts.SyntaxKind.VariableStatement:
|
|
||||||
const variableStatement = <ts.VariableStatement>node;
|
|
||||||
for (const variableDeclaration of variableStatement.declarationList.declarations) {
|
|
||||||
if (variableDeclaration.name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const nameNode = <ts.Identifier>variableDeclaration.name;
|
|
||||||
let varValue: MetadataValue;
|
|
||||||
if (variableDeclaration.initializer) {
|
|
||||||
varValue = evaluator.evaluateNode(variableDeclaration.initializer);
|
|
||||||
} else {
|
|
||||||
varValue = recordEntry(errorSym('Variable not initialized', nameNode), nameNode);
|
|
||||||
}
|
|
||||||
let exported = false;
|
|
||||||
if (isExport(variableStatement) || isExport(variableDeclaration) ||
|
|
||||||
isExportedIdentifier(nameNode)) {
|
|
||||||
const name = exportedIdentifierName(nameNode);
|
|
||||||
if (name) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name] = recordEntry(varValue, node);
|
|
||||||
}
|
|
||||||
exported = true;
|
|
||||||
}
|
|
||||||
if (typeof varValue == 'string' || typeof varValue == 'number' ||
|
|
||||||
typeof varValue == 'boolean') {
|
|
||||||
locals.define(nameNode.text, varValue);
|
|
||||||
if (exported) {
|
|
||||||
locals.defineReference(
|
|
||||||
nameNode.text, {__symbolic: 'reference', name: nameNode.text});
|
|
||||||
}
|
|
||||||
} else if (!exported) {
|
|
||||||
if (varValue && !isMetadataError(varValue)) {
|
|
||||||
locals.define(nameNode.text, recordEntry(varValue, node));
|
|
||||||
} else {
|
|
||||||
locals.define(
|
|
||||||
nameNode.text,
|
|
||||||
recordEntry(
|
|
||||||
errorSym('Reference to a local symbol', nameNode, {name: nameNode.text}),
|
|
||||||
node));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Destructuring (or binding) declarations are not supported,
|
|
||||||
// var {<identifier>[, <identifier>]+} = <expression>;
|
|
||||||
// or
|
|
||||||
// var [<identifier>[, <identifier}+] = <expression>;
|
|
||||||
// are not supported.
|
|
||||||
const report: (nameNode: ts.Node) => void = (nameNode: ts.Node) => {
|
|
||||||
switch (nameNode.kind) {
|
|
||||||
case ts.SyntaxKind.Identifier:
|
|
||||||
const name = <ts.Identifier>nameNode;
|
|
||||||
const varValue = errorSym('Destructuring not supported', name);
|
|
||||||
locals.define(name.text, varValue);
|
|
||||||
if (isExport(node)) {
|
|
||||||
if (!metadata) metadata = {};
|
|
||||||
metadata[name.text] = varValue;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.BindingElement:
|
|
||||||
const bindingElement = <ts.BindingElement>nameNode;
|
|
||||||
report(bindingElement.name);
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.ObjectBindingPattern:
|
|
||||||
case ts.SyntaxKind.ArrayBindingPattern:
|
|
||||||
const bindings = <ts.BindingPattern>nameNode;
|
|
||||||
(bindings as any).elements.forEach(report);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
report(variableDeclaration.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (metadata || exports) {
|
|
||||||
if (!metadata)
|
|
||||||
metadata = {};
|
|
||||||
else if (strict) {
|
|
||||||
validateMetadata(sourceFile, nodeMap, metadata);
|
|
||||||
}
|
|
||||||
const result: ModuleMetadata = {
|
|
||||||
__symbolic: 'module',
|
|
||||||
version: this.options.version || VERSION, metadata
|
|
||||||
};
|
|
||||||
if (exports) result.exports = exports;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This will throw if the metadata entry given contains an error node.
|
|
||||||
function validateMetadata(
|
|
||||||
sourceFile: ts.SourceFile, nodeMap: Map<MetadataEntry, ts.Node>,
|
|
||||||
metadata: {[name: string]: MetadataEntry}) {
|
|
||||||
let locals: Set<string> = new Set(['Array', 'Object', 'Set', 'Map', 'string', 'number', 'any']);
|
|
||||||
|
|
||||||
function validateExpression(
|
|
||||||
expression: MetadataValue | MetadataSymbolicExpression | MetadataError) {
|
|
||||||
if (!expression) {
|
|
||||||
return;
|
|
||||||
} else if (Array.isArray(expression)) {
|
|
||||||
expression.forEach(validateExpression);
|
|
||||||
} else if (typeof expression === 'object' && !expression.hasOwnProperty('__symbolic')) {
|
|
||||||
Object.getOwnPropertyNames(expression).forEach(v => validateExpression((<any>expression)[v]));
|
|
||||||
} else if (isMetadataError(expression)) {
|
|
||||||
reportError(expression);
|
|
||||||
} else if (isMetadataGlobalReferenceExpression(expression)) {
|
|
||||||
if (!locals.has(expression.name)) {
|
|
||||||
const reference = <MetadataValue>metadata[expression.name];
|
|
||||||
if (reference) {
|
|
||||||
validateExpression(reference);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (isFunctionMetadata(expression)) {
|
|
||||||
validateFunction(<any>expression);
|
|
||||||
} else if (isMetadataSymbolicExpression(expression)) {
|
|
||||||
switch (expression.__symbolic) {
|
|
||||||
case 'binary':
|
|
||||||
const binaryExpression = <MetadataSymbolicBinaryExpression>expression;
|
|
||||||
validateExpression(binaryExpression.left);
|
|
||||||
validateExpression(binaryExpression.right);
|
|
||||||
break;
|
|
||||||
case 'call':
|
|
||||||
case 'new':
|
|
||||||
const callExpression = <MetadataSymbolicCallExpression>expression;
|
|
||||||
validateExpression(callExpression.expression);
|
|
||||||
if (callExpression.arguments) callExpression.arguments.forEach(validateExpression);
|
|
||||||
break;
|
|
||||||
case 'index':
|
|
||||||
const indexExpression = <MetadataSymbolicIndexExpression>expression;
|
|
||||||
validateExpression(indexExpression.expression);
|
|
||||||
validateExpression(indexExpression.index);
|
|
||||||
break;
|
|
||||||
case 'pre':
|
|
||||||
const prefixExpression = <MetadataSymbolicPrefixExpression>expression;
|
|
||||||
validateExpression(prefixExpression.operand);
|
|
||||||
break;
|
|
||||||
case 'select':
|
|
||||||
const selectExpression = <MetadataSymbolicSelectExpression>expression;
|
|
||||||
validateExpression(selectExpression.expression);
|
|
||||||
break;
|
|
||||||
case 'spread':
|
|
||||||
const spreadExpression = <MetadataSymbolicSpreadExpression>expression;
|
|
||||||
validateExpression(spreadExpression.expression);
|
|
||||||
break;
|
|
||||||
case 'if':
|
|
||||||
const ifExpression = <MetadataSymbolicIfExpression>expression;
|
|
||||||
validateExpression(ifExpression.condition);
|
|
||||||
validateExpression(ifExpression.elseExpression);
|
|
||||||
validateExpression(ifExpression.thenExpression);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateMember(classData: ClassMetadata, member: MemberMetadata) {
|
|
||||||
if (member.decorators) {
|
|
||||||
member.decorators.forEach(validateExpression);
|
|
||||||
}
|
|
||||||
if (isMethodMetadata(member) && member.parameterDecorators) {
|
|
||||||
member.parameterDecorators.forEach(validateExpression);
|
|
||||||
}
|
|
||||||
// Only validate parameters of classes for which we know that are used with our DI
|
|
||||||
if (classData.decorators && isConstructorMetadata(member) && member.parameters) {
|
|
||||||
member.parameters.forEach(validateExpression);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateClass(classData: ClassMetadata) {
|
|
||||||
if (classData.decorators) {
|
|
||||||
classData.decorators.forEach(validateExpression);
|
|
||||||
}
|
|
||||||
if (classData.members) {
|
|
||||||
Object.getOwnPropertyNames(classData.members)
|
|
||||||
.forEach(name => classData.members ![name].forEach((m) => validateMember(classData, m)));
|
|
||||||
}
|
|
||||||
if (classData.statics) {
|
|
||||||
Object.getOwnPropertyNames(classData.statics).forEach(name => {
|
|
||||||
const staticMember = classData.statics ![name];
|
|
||||||
if (isFunctionMetadata(staticMember)) {
|
|
||||||
validateExpression(staticMember.value);
|
|
||||||
} else {
|
|
||||||
validateExpression(staticMember);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateFunction(functionDeclaration: FunctionMetadata) {
|
|
||||||
if (functionDeclaration.value) {
|
|
||||||
const oldLocals = locals;
|
|
||||||
if (functionDeclaration.parameters) {
|
|
||||||
locals = new Set(oldLocals.values());
|
|
||||||
if (functionDeclaration.parameters)
|
|
||||||
functionDeclaration.parameters.forEach(n => locals.add(n));
|
|
||||||
}
|
|
||||||
validateExpression(functionDeclaration.value);
|
|
||||||
locals = oldLocals;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function shouldReportNode(node: ts.Node | undefined) {
|
|
||||||
if (node) {
|
|
||||||
const nodeStart = node.getStart();
|
|
||||||
return !(
|
|
||||||
node.pos != nodeStart &&
|
|
||||||
sourceFile.text.substring(node.pos, nodeStart).indexOf('@dynamic') >= 0);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function reportError(error: MetadataError) {
|
|
||||||
const node = nodeMap.get(error);
|
|
||||||
if (shouldReportNode(node)) {
|
|
||||||
const lineInfo = error.line != undefined ?
|
|
||||||
error.character != undefined ? `:${error.line + 1}:${error.character + 1}` :
|
|
||||||
`:${error.line + 1}` :
|
|
||||||
'';
|
|
||||||
throw new Error(
|
|
||||||
`${sourceFile.fileName}${lineInfo}: Metadata collected contains an error that will be reported at runtime: ${expandedMessage(error)}.\n ${JSON.stringify(error)}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Object.getOwnPropertyNames(metadata).forEach(name => {
|
|
||||||
const entry = metadata[name];
|
|
||||||
try {
|
|
||||||
if (isClassMetadata(entry)) {
|
|
||||||
validateClass(entry);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
const node = nodeMap.get(entry);
|
|
||||||
if (shouldReportNode(node)) {
|
|
||||||
if (node) {
|
|
||||||
const {line, character} = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
||||||
throw new Error(
|
|
||||||
`${sourceFile.fileName}:${line + 1}:${character + 1}: Error encountered in metadata generated for exported symbol '${name}': \n ${e.message}`);
|
|
||||||
}
|
|
||||||
throw new Error(
|
|
||||||
`Error encountered in metadata generated for exported symbol ${name}: \n ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect parameter names from a function.
|
|
||||||
function namesOf(parameters: ts.NodeArray<ts.ParameterDeclaration>): string[] {
|
|
||||||
const result: string[] = [];
|
|
||||||
|
|
||||||
function addNamesOf(name: ts.Identifier | ts.BindingPattern) {
|
|
||||||
if (name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
const identifier = <ts.Identifier>name;
|
|
||||||
result.push(identifier.text);
|
|
||||||
} else {
|
|
||||||
const bindingPattern = <ts.BindingPattern>name;
|
|
||||||
for (const element of bindingPattern.elements) {
|
|
||||||
const name = (element as any).name;
|
|
||||||
if (name) {
|
|
||||||
addNamesOf(name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const parameter of parameters) {
|
|
||||||
addNamesOf(parameter.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function expandedMessage(error: any): string {
|
|
||||||
switch (error.message) {
|
|
||||||
case 'Reference to non-exported class':
|
|
||||||
if (error.context && error.context.className) {
|
|
||||||
return `Reference to a non-exported class ${error.context.className}. Consider exporting the class`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Variable not initialized':
|
|
||||||
return 'Only initialized variables and constants can be referenced because the value of this variable is needed by the template compiler';
|
|
||||||
case 'Destructuring not supported':
|
|
||||||
return 'Referencing an exported destructured variable or constant is not supported by the template compiler. Consider simplifying this to avoid destructuring';
|
|
||||||
case 'Could not resolve type':
|
|
||||||
if (error.context && error.context.typeName) {
|
|
||||||
return `Could not resolve type ${error.context.typeName}`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Function call not supported':
|
|
||||||
let prefix =
|
|
||||||
error.context && error.context.name ? `Calling function '${error.context.name}', f` : 'F';
|
|
||||||
return prefix +
|
|
||||||
'unction calls are not supported. Consider replacing the function or lambda with a reference to an exported function';
|
|
||||||
case 'Reference to a local symbol':
|
|
||||||
if (error.context && error.context.name) {
|
|
||||||
return `Reference to a local (non-exported) symbol '${error.context.name}'. Consider exporting the symbol`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return error.message;
|
|
||||||
}
|
|
@ -1,159 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {writeFileSync} from 'fs';
|
|
||||||
import {normalize} from 'path';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import NgOptions from './options';
|
|
||||||
import {MetadataCollector} from './collector';
|
|
||||||
import {ModuleMetadata} from './schema';
|
|
||||||
|
|
||||||
export function formatDiagnostics(d: ts.Diagnostic[]): string {
|
|
||||||
const host: ts.FormatDiagnosticsHost = {
|
|
||||||
getCurrentDirectory: () => ts.sys.getCurrentDirectory(),
|
|
||||||
getNewLine: () => ts.sys.newLine,
|
|
||||||
getCanonicalFileName: (f: string) => f
|
|
||||||
};
|
|
||||||
return ts.formatDiagnostics(d, host);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Implementation of CompilerHost that forwards all methods to another instance.
|
|
||||||
* Useful for partial implementations to override only methods they care about.
|
|
||||||
*/
|
|
||||||
export abstract class DelegatingHost implements ts.CompilerHost {
|
|
||||||
constructor(protected delegate: ts.CompilerHost) {}
|
|
||||||
getSourceFile =
|
|
||||||
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) =>
|
|
||||||
this.delegate.getSourceFile(fileName, languageVersion, onError);
|
|
||||||
|
|
||||||
getCancellationToken = () => this.delegate.getCancellationToken !();
|
|
||||||
getDefaultLibFileName = (options: ts.CompilerOptions) =>
|
|
||||||
this.delegate.getDefaultLibFileName(options);
|
|
||||||
getDefaultLibLocation = () => this.delegate.getDefaultLibLocation !();
|
|
||||||
writeFile: ts.WriteFileCallback = this.delegate.writeFile;
|
|
||||||
getCurrentDirectory = () => this.delegate.getCurrentDirectory();
|
|
||||||
getDirectories = (path: string): string[] =>
|
|
||||||
(this.delegate as any).getDirectories?(this.delegate as any).getDirectories(path): [];
|
|
||||||
getCanonicalFileName = (fileName: string) => this.delegate.getCanonicalFileName(fileName);
|
|
||||||
useCaseSensitiveFileNames = () => this.delegate.useCaseSensitiveFileNames();
|
|
||||||
getNewLine = () => this.delegate.getNewLine();
|
|
||||||
fileExists = (fileName: string) => this.delegate.fileExists(fileName);
|
|
||||||
readFile = (fileName: string) => this.delegate.readFile(fileName);
|
|
||||||
trace = (s: string) => this.delegate.trace !(s);
|
|
||||||
directoryExists = (directoryName: string) => this.delegate.directoryExists !(directoryName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const IGNORED_FILES = /\.ngfactory\.js$|\.ngstyle\.js$/;
|
|
||||||
const DTS = /\.d\.ts$/;
|
|
||||||
|
|
||||||
export class MetadataWriterHost extends DelegatingHost {
|
|
||||||
private metadataCollector = new MetadataCollector({quotedNames: true});
|
|
||||||
private metadataCollector1 = new MetadataCollector({version: 1});
|
|
||||||
constructor(
|
|
||||||
delegate: ts.CompilerHost, private ngOptions: NgOptions, private emitAllFiles: boolean) {
|
|
||||||
super(delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
private writeMetadata(emitFilePath: string, sourceFile: ts.SourceFile) {
|
|
||||||
// TODO: replace with DTS filePath when https://github.com/Microsoft/TypeScript/pull/8412 is
|
|
||||||
// released
|
|
||||||
if (/*DTS*/ /\.js$/.test(emitFilePath)) {
|
|
||||||
const path = emitFilePath.replace(/*DTS*/ /\.js$/, '.metadata.json');
|
|
||||||
|
|
||||||
// Beginning with 2.1, TypeScript transforms the source tree before emitting it.
|
|
||||||
// We need the original, unmodified, tree which might be several levels back
|
|
||||||
// depending on the number of transforms performed. All SourceFile's prior to 2.1
|
|
||||||
// will appear to be the original source since they didn't include an original field.
|
|
||||||
let collectableFile = sourceFile;
|
|
||||||
while ((collectableFile as any).original) {
|
|
||||||
collectableFile = (collectableFile as any).original;
|
|
||||||
}
|
|
||||||
|
|
||||||
const metadata =
|
|
||||||
this.metadataCollector.getMetadata(collectableFile, !!this.ngOptions.strictMetadataEmit);
|
|
||||||
const metadata1 = this.metadataCollector1.getMetadata(collectableFile, false);
|
|
||||||
const metadatas: ModuleMetadata[] =
|
|
||||||
[metadata, metadata1].filter(e => !!e) as ModuleMetadata[];
|
|
||||||
if (metadatas.length) {
|
|
||||||
const metadataText = JSON.stringify(metadatas);
|
|
||||||
writeFileSync(path, metadataText, {encoding: 'utf-8'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
writeFile: ts.WriteFileCallback =
|
|
||||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
|
||||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
|
||||||
const isDts = /\.d\.ts$/.test(fileName);
|
|
||||||
if (this.emitAllFiles || isDts) {
|
|
||||||
// Let the original file be written first; this takes care of creating parent directories
|
|
||||||
this.delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
|
||||||
}
|
|
||||||
if (isDts) {
|
|
||||||
// TODO: remove this early return after https://github.com/Microsoft/TypeScript/pull/8412
|
|
||||||
// is released
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (IGNORED_FILES.test(fileName)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sourceFiles) {
|
|
||||||
throw new Error(
|
|
||||||
'Metadata emit requires the sourceFiles are passed to WriteFileCallback. ' +
|
|
||||||
'Update to TypeScript ^1.9.0-dev');
|
|
||||||
}
|
|
||||||
if (sourceFiles.length > 1) {
|
|
||||||
throw new Error('Bundled emit with --out is not supported');
|
|
||||||
}
|
|
||||||
if (!this.ngOptions.skipMetadataEmit && !this.ngOptions.flatModuleOutFile) {
|
|
||||||
this.writeMetadata(fileName, sourceFiles[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function createSyntheticIndexHost<H extends ts.CompilerHost>(
|
|
||||||
delegate: H, syntheticIndex: {name: string, content: string, metadata: string}): H {
|
|
||||||
const normalSyntheticIndexName = normalize(syntheticIndex.name);
|
|
||||||
const indexContent = syntheticIndex.content;
|
|
||||||
const indexMetadata = syntheticIndex.metadata;
|
|
||||||
|
|
||||||
const newHost = Object.create(delegate);
|
|
||||||
newHost.fileExists = (fileName: string): boolean => {
|
|
||||||
return normalize(fileName) == normalSyntheticIndexName || delegate.fileExists(fileName);
|
|
||||||
};
|
|
||||||
|
|
||||||
newHost.readFile = (fileName: string) => {
|
|
||||||
return normalize(fileName) == normalSyntheticIndexName ? indexContent :
|
|
||||||
delegate.readFile(fileName);
|
|
||||||
};
|
|
||||||
|
|
||||||
newHost.getSourceFile =
|
|
||||||
(fileName: string, languageVersion: ts.ScriptTarget, onError?: (message: string) => void) => {
|
|
||||||
if (normalize(fileName) == normalSyntheticIndexName) {
|
|
||||||
return ts.createSourceFile(fileName, indexContent, languageVersion, true);
|
|
||||||
}
|
|
||||||
return delegate.getSourceFile(fileName, languageVersion, onError);
|
|
||||||
};
|
|
||||||
|
|
||||||
newHost.writeFile =
|
|
||||||
(fileName: string, data: string, writeByteOrderMark: boolean,
|
|
||||||
onError?: (message: string) => void, sourceFiles?: ts.SourceFile[]) => {
|
|
||||||
delegate.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles);
|
|
||||||
if (fileName.match(DTS) && sourceFiles && sourceFiles.length == 1 &&
|
|
||||||
normalize(sourceFiles[0].fileName) == normalSyntheticIndexName) {
|
|
||||||
// If we are writing the synthetic index, write the metadata along side.
|
|
||||||
const metadataName = fileName.replace(DTS, '.metadata.json');
|
|
||||||
writeFileSync(metadataName, indexMetadata, {encoding: 'utf8'});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
return newHost;
|
|
||||||
}
|
|
@ -1,680 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {CollectorOptions} from './collector';
|
|
||||||
import {MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
|
|
||||||
import {Symbols} from './symbols';
|
|
||||||
|
|
||||||
|
|
||||||
// In TypeScript 2.1 the spread element kind was renamed.
|
|
||||||
const spreadElementSyntaxKind: ts.SyntaxKind =
|
|
||||||
(ts.SyntaxKind as any).SpreadElement || (ts.SyntaxKind as any).SpreadElementExpression;
|
|
||||||
|
|
||||||
function isMethodCallOf(callExpression: ts.CallExpression, memberName: string): boolean {
|
|
||||||
const expression = callExpression.expression;
|
|
||||||
if (expression.kind === ts.SyntaxKind.PropertyAccessExpression) {
|
|
||||||
const propertyAccessExpression = <ts.PropertyAccessExpression>expression;
|
|
||||||
const name = propertyAccessExpression.name;
|
|
||||||
if (name.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
return name.text === memberName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCallOf(callExpression: ts.CallExpression, ident: string): boolean {
|
|
||||||
const expression = callExpression.expression;
|
|
||||||
if (expression.kind === ts.SyntaxKind.Identifier) {
|
|
||||||
const identifier = <ts.Identifier>expression;
|
|
||||||
return identifier.text === ident;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* ts.forEachChild stops iterating children when the callback return a truthy value.
|
|
||||||
* This method inverts this to implement an `every` style iterator. It will return
|
|
||||||
* true if every call to `cb` returns `true`.
|
|
||||||
*/
|
|
||||||
function everyNodeChild(node: ts.Node, cb: (node: ts.Node) => boolean) {
|
|
||||||
return !ts.forEachChild(node, node => !cb(node));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isPrimitive(value: any): boolean {
|
|
||||||
return Object(value) !== value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isDefined(obj: any): boolean {
|
|
||||||
return obj !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
// import {propertyName as name} from 'place'
|
|
||||||
// import {name} from 'place'
|
|
||||||
export interface ImportSpecifierMetadata {
|
|
||||||
name: string;
|
|
||||||
propertyName?: string;
|
|
||||||
}
|
|
||||||
export interface ImportMetadata {
|
|
||||||
defaultName?: string; // import d from 'place'
|
|
||||||
namespace?: string; // import * as d from 'place'
|
|
||||||
namedImports?: ImportSpecifierMetadata[]; // import {a} from 'place'
|
|
||||||
from: string; // from 'place'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile {
|
|
||||||
while (node && node.kind != ts.SyntaxKind.SourceFile) {
|
|
||||||
node = node.parent;
|
|
||||||
}
|
|
||||||
return <ts.SourceFile>node;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* @internal */
|
|
||||||
export function errorSymbol(
|
|
||||||
message: string, node?: ts.Node, context?: {[name: string]: string},
|
|
||||||
sourceFile?: ts.SourceFile): MetadataError {
|
|
||||||
let result: MetadataError|undefined = undefined;
|
|
||||||
if (node) {
|
|
||||||
sourceFile = sourceFile || getSourceFileOfNode(node);
|
|
||||||
if (sourceFile) {
|
|
||||||
const {line, character} =
|
|
||||||
ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
|
|
||||||
result = {__symbolic: 'error', message, line, character};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!result) {
|
|
||||||
result = {__symbolic: 'error', message};
|
|
||||||
}
|
|
||||||
if (context) {
|
|
||||||
result.context = context;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce a symbolic representation of an expression folding values into their final value when
|
|
||||||
* possible.
|
|
||||||
*/
|
|
||||||
export class Evaluator {
|
|
||||||
constructor(
|
|
||||||
private symbols: Symbols, private nodeMap: Map<MetadataEntry, ts.Node>,
|
|
||||||
private options: CollectorOptions = {},
|
|
||||||
private recordExport?: (name: string, value: MetadataValue) => void) {}
|
|
||||||
|
|
||||||
nameOf(node: ts.Node|undefined): string|MetadataError {
|
|
||||||
if (node && node.kind == ts.SyntaxKind.Identifier) {
|
|
||||||
return (<ts.Identifier>node).text;
|
|
||||||
}
|
|
||||||
const result = node && this.evaluateNode(node);
|
|
||||||
if (isMetadataError(result) || typeof result === 'string') {
|
|
||||||
return result;
|
|
||||||
} else {
|
|
||||||
return errorSymbol(
|
|
||||||
'Name expected', node, {received: (node && node.getText()) || '<missing>'});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the expression represented by `node` can be folded into a literal expression.
|
|
||||||
*
|
|
||||||
* For example, a literal is always foldable. This means that literal expressions such as `1.2`
|
|
||||||
* `"Some value"` `true` `false` are foldable.
|
|
||||||
*
|
|
||||||
* - An object literal is foldable if all the properties in the literal are foldable.
|
|
||||||
* - An array literal is foldable if all the elements are foldable.
|
|
||||||
* - A call is foldable if it is a call to a Array.prototype.concat or a call to CONST_EXPR.
|
|
||||||
* - A property access is foldable if the object is foldable.
|
|
||||||
* - A array index is foldable if index expression is foldable and the array is foldable.
|
|
||||||
* - Binary operator expressions are foldable if the left and right expressions are foldable and
|
|
||||||
* it is one of '+', '-', '*', '/', '%', '||', and '&&'.
|
|
||||||
* - An identifier is foldable if a value can be found for its symbol in the evaluator symbol
|
|
||||||
* table.
|
|
||||||
*/
|
|
||||||
public isFoldable(node: ts.Node): boolean {
|
|
||||||
return this.isFoldableWorker(node, new Map<ts.Node, boolean>());
|
|
||||||
}
|
|
||||||
|
|
||||||
private isFoldableWorker(node: ts.Node|undefined, folding: Map<ts.Node, boolean>): boolean {
|
|
||||||
if (node) {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ObjectLiteralExpression:
|
|
||||||
return everyNodeChild(node, child => {
|
|
||||||
if (child.kind === ts.SyntaxKind.PropertyAssignment) {
|
|
||||||
const propertyAssignment = <ts.PropertyAssignment>child;
|
|
||||||
return this.isFoldableWorker(propertyAssignment.initializer, folding);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
});
|
|
||||||
case ts.SyntaxKind.ArrayLiteralExpression:
|
|
||||||
return everyNodeChild(node, child => this.isFoldableWorker(child, folding));
|
|
||||||
case ts.SyntaxKind.CallExpression:
|
|
||||||
const callExpression = <ts.CallExpression>node;
|
|
||||||
// We can fold a <array>.concat(<v>).
|
|
||||||
if (isMethodCallOf(callExpression, 'concat') &&
|
|
||||||
arrayOrEmpty(callExpression.arguments).length === 1) {
|
|
||||||
const arrayNode = (<ts.PropertyAccessExpression>callExpression.expression).expression;
|
|
||||||
if (this.isFoldableWorker(arrayNode, folding) &&
|
|
||||||
this.isFoldableWorker(callExpression.arguments[0], folding)) {
|
|
||||||
// It needs to be an array.
|
|
||||||
const arrayValue = this.evaluateNode(arrayNode);
|
|
||||||
if (arrayValue && Array.isArray(arrayValue)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We can fold a call to CONST_EXPR
|
|
||||||
if (isCallOf(callExpression, 'CONST_EXPR') &&
|
|
||||||
arrayOrEmpty(callExpression.arguments).length === 1)
|
|
||||||
return this.isFoldableWorker(callExpression.arguments[0], folding);
|
|
||||||
return false;
|
|
||||||
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
|
||||||
case ts.SyntaxKind.StringLiteral:
|
|
||||||
case ts.SyntaxKind.NumericLiteral:
|
|
||||||
case ts.SyntaxKind.NullKeyword:
|
|
||||||
case ts.SyntaxKind.TrueKeyword:
|
|
||||||
case ts.SyntaxKind.FalseKeyword:
|
|
||||||
case ts.SyntaxKind.TemplateHead:
|
|
||||||
case ts.SyntaxKind.TemplateMiddle:
|
|
||||||
case ts.SyntaxKind.TemplateTail:
|
|
||||||
return true;
|
|
||||||
case ts.SyntaxKind.ParenthesizedExpression:
|
|
||||||
const parenthesizedExpression = <ts.ParenthesizedExpression>node;
|
|
||||||
return this.isFoldableWorker(parenthesizedExpression.expression, folding);
|
|
||||||
case ts.SyntaxKind.BinaryExpression:
|
|
||||||
const binaryExpression = <ts.BinaryExpression>node;
|
|
||||||
switch (binaryExpression.operatorToken.kind) {
|
|
||||||
case ts.SyntaxKind.PlusToken:
|
|
||||||
case ts.SyntaxKind.MinusToken:
|
|
||||||
case ts.SyntaxKind.AsteriskToken:
|
|
||||||
case ts.SyntaxKind.SlashToken:
|
|
||||||
case ts.SyntaxKind.PercentToken:
|
|
||||||
case ts.SyntaxKind.AmpersandAmpersandToken:
|
|
||||||
case ts.SyntaxKind.BarBarToken:
|
|
||||||
return this.isFoldableWorker(binaryExpression.left, folding) &&
|
|
||||||
this.isFoldableWorker(binaryExpression.right, folding);
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
case ts.SyntaxKind.PropertyAccessExpression:
|
|
||||||
const propertyAccessExpression = <ts.PropertyAccessExpression>node;
|
|
||||||
return this.isFoldableWorker(propertyAccessExpression.expression, folding);
|
|
||||||
case ts.SyntaxKind.ElementAccessExpression:
|
|
||||||
const elementAccessExpression = <ts.ElementAccessExpression>node;
|
|
||||||
return this.isFoldableWorker(elementAccessExpression.expression, folding) &&
|
|
||||||
this.isFoldableWorker(elementAccessExpression.argumentExpression, folding);
|
|
||||||
case ts.SyntaxKind.Identifier:
|
|
||||||
let identifier = <ts.Identifier>node;
|
|
||||||
let reference = this.symbols.resolve(identifier.text);
|
|
||||||
if (reference !== undefined && isPrimitive(reference)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.TemplateExpression:
|
|
||||||
const templateExpression = <ts.TemplateExpression>node;
|
|
||||||
return templateExpression.templateSpans.every(
|
|
||||||
span => this.isFoldableWorker(span.expression, folding));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produce a JSON serialiable object representing `node`. The foldable values in the expression
|
|
||||||
* tree are folded. For example, a node representing `1 + 2` is folded into `3`.
|
|
||||||
*/
|
|
||||||
public evaluateNode(node: ts.Node, preferReference?: boolean): MetadataValue {
|
|
||||||
const t = this;
|
|
||||||
let error: MetadataError|undefined;
|
|
||||||
|
|
||||||
function recordEntry(entry: MetadataValue, node: ts.Node): MetadataValue {
|
|
||||||
if (t.options.substituteExpression) {
|
|
||||||
const newEntry = t.options.substituteExpression(entry, node);
|
|
||||||
if (t.recordExport && newEntry != entry && isMetadataGlobalReferenceExpression(newEntry)) {
|
|
||||||
t.recordExport(newEntry.name, entry);
|
|
||||||
}
|
|
||||||
entry = newEntry;
|
|
||||||
}
|
|
||||||
t.nodeMap.set(entry, node);
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isFoldableError(value: any): value is MetadataError {
|
|
||||||
return !t.options.verboseInvalidExpression && isMetadataError(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const resolveName = (name: string, preferReference?: boolean): MetadataValue => {
|
|
||||||
const reference = this.symbols.resolve(name, preferReference);
|
|
||||||
if (reference === undefined) {
|
|
||||||
// Encode as a global reference. StaticReflector will check the reference.
|
|
||||||
return recordEntry({__symbolic: 'reference', name}, node);
|
|
||||||
}
|
|
||||||
return reference;
|
|
||||||
};
|
|
||||||
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ObjectLiteralExpression:
|
|
||||||
let obj: {[name: string]: any} = {};
|
|
||||||
let quoted: string[] = [];
|
|
||||||
ts.forEachChild(node, child => {
|
|
||||||
switch (child.kind) {
|
|
||||||
case ts.SyntaxKind.ShorthandPropertyAssignment:
|
|
||||||
case ts.SyntaxKind.PropertyAssignment:
|
|
||||||
const assignment = <ts.PropertyAssignment|ts.ShorthandPropertyAssignment>child;
|
|
||||||
if (assignment.name.kind == ts.SyntaxKind.StringLiteral) {
|
|
||||||
const name = (assignment.name as ts.StringLiteral).text;
|
|
||||||
quoted.push(name);
|
|
||||||
}
|
|
||||||
const propertyName = this.nameOf(assignment.name);
|
|
||||||
if (isFoldableError(propertyName)) {
|
|
||||||
error = propertyName;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
const propertyValue = isPropertyAssignment(assignment) ?
|
|
||||||
this.evaluateNode(assignment.initializer, /* preferReference */ true) :
|
|
||||||
resolveName(propertyName, /* preferReference */ true);
|
|
||||||
if (isFoldableError(propertyValue)) {
|
|
||||||
error = propertyValue;
|
|
||||||
return true; // Stop the forEachChild.
|
|
||||||
} else {
|
|
||||||
obj[<string>propertyName] = isPropertyAssignment(assignment) ?
|
|
||||||
recordEntry(propertyValue, assignment.initializer) :
|
|
||||||
propertyValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (error) return error;
|
|
||||||
if (this.options.quotedNames && quoted.length) {
|
|
||||||
obj['$quoted$'] = quoted;
|
|
||||||
}
|
|
||||||
return recordEntry(obj, node);
|
|
||||||
case ts.SyntaxKind.ArrayLiteralExpression:
|
|
||||||
let arr: MetadataValue[] = [];
|
|
||||||
ts.forEachChild(node, child => {
|
|
||||||
const value = this.evaluateNode(child, /* preferReference */ true);
|
|
||||||
|
|
||||||
// Check for error
|
|
||||||
if (isFoldableError(value)) {
|
|
||||||
error = value;
|
|
||||||
return true; // Stop the forEachChild.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle spread expressions
|
|
||||||
if (isMetadataSymbolicSpreadExpression(value)) {
|
|
||||||
if (Array.isArray(value.expression)) {
|
|
||||||
for (const spreadValue of value.expression) {
|
|
||||||
arr.push(spreadValue);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
arr.push(value);
|
|
||||||
});
|
|
||||||
if (error) return error;
|
|
||||||
return recordEntry(arr, node);
|
|
||||||
case spreadElementSyntaxKind:
|
|
||||||
let spreadExpression = this.evaluateNode((node as any).expression);
|
|
||||||
return recordEntry({__symbolic: 'spread', expression: spreadExpression}, node);
|
|
||||||
case ts.SyntaxKind.CallExpression:
|
|
||||||
const callExpression = <ts.CallExpression>node;
|
|
||||||
if (isCallOf(callExpression, 'forwardRef') &&
|
|
||||||
arrayOrEmpty(callExpression.arguments).length === 1) {
|
|
||||||
const firstArgument = callExpression.arguments[0];
|
|
||||||
if (firstArgument.kind == ts.SyntaxKind.ArrowFunction) {
|
|
||||||
const arrowFunction = <ts.ArrowFunction>firstArgument;
|
|
||||||
return recordEntry(this.evaluateNode(arrowFunction.body), node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const args = arrayOrEmpty(callExpression.arguments).map(arg => this.evaluateNode(arg));
|
|
||||||
if (!this.options.verboseInvalidExpression && args.some(isMetadataError)) {
|
|
||||||
return args.find(isMetadataError);
|
|
||||||
}
|
|
||||||
if (this.isFoldable(callExpression)) {
|
|
||||||
if (isMethodCallOf(callExpression, 'concat')) {
|
|
||||||
const arrayValue = <MetadataValue[]>this.evaluateNode(
|
|
||||||
(<ts.PropertyAccessExpression>callExpression.expression).expression);
|
|
||||||
if (isFoldableError(arrayValue)) return arrayValue;
|
|
||||||
return arrayValue.concat(args[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Always fold a CONST_EXPR even if the argument is not foldable.
|
|
||||||
if (isCallOf(callExpression, 'CONST_EXPR') &&
|
|
||||||
arrayOrEmpty(callExpression.arguments).length === 1) {
|
|
||||||
return recordEntry(args[0], node);
|
|
||||||
}
|
|
||||||
const expression = this.evaluateNode(callExpression.expression);
|
|
||||||
if (isFoldableError(expression)) {
|
|
||||||
return recordEntry(expression, node);
|
|
||||||
}
|
|
||||||
let result: MetadataSymbolicCallExpression = {__symbolic: 'call', expression: expression};
|
|
||||||
if (args && args.length) {
|
|
||||||
result.arguments = args;
|
|
||||||
}
|
|
||||||
return recordEntry(result, node);
|
|
||||||
case ts.SyntaxKind.NewExpression:
|
|
||||||
const newExpression = <ts.NewExpression>node;
|
|
||||||
const newArgs = arrayOrEmpty(newExpression.arguments).map(arg => this.evaluateNode(arg));
|
|
||||||
if (!this.options.verboseInvalidExpression && newArgs.some(isMetadataError)) {
|
|
||||||
return recordEntry(newArgs.find(isMetadataError), node);
|
|
||||||
}
|
|
||||||
const newTarget = this.evaluateNode(newExpression.expression);
|
|
||||||
if (isMetadataError(newTarget)) {
|
|
||||||
return recordEntry(newTarget, node);
|
|
||||||
}
|
|
||||||
const call: MetadataSymbolicCallExpression = {__symbolic: 'new', expression: newTarget};
|
|
||||||
if (newArgs.length) {
|
|
||||||
call.arguments = newArgs;
|
|
||||||
}
|
|
||||||
return recordEntry(call, node);
|
|
||||||
case ts.SyntaxKind.PropertyAccessExpression: {
|
|
||||||
const propertyAccessExpression = <ts.PropertyAccessExpression>node;
|
|
||||||
const expression = this.evaluateNode(propertyAccessExpression.expression);
|
|
||||||
if (isFoldableError(expression)) {
|
|
||||||
return recordEntry(expression, node);
|
|
||||||
}
|
|
||||||
const member = this.nameOf(propertyAccessExpression.name);
|
|
||||||
if (isFoldableError(member)) {
|
|
||||||
return recordEntry(member, node);
|
|
||||||
}
|
|
||||||
if (expression && this.isFoldable(propertyAccessExpression.expression))
|
|
||||||
return (<any>expression)[<string>member];
|
|
||||||
if (isMetadataModuleReferenceExpression(expression)) {
|
|
||||||
// A select into a module reference and be converted into a reference to the symbol
|
|
||||||
// in the module
|
|
||||||
return recordEntry(
|
|
||||||
{__symbolic: 'reference', module: expression.module, name: member}, node);
|
|
||||||
}
|
|
||||||
return recordEntry({__symbolic: 'select', expression, member}, node);
|
|
||||||
}
|
|
||||||
case ts.SyntaxKind.ElementAccessExpression: {
|
|
||||||
const elementAccessExpression = <ts.ElementAccessExpression>node;
|
|
||||||
const expression = this.evaluateNode(elementAccessExpression.expression);
|
|
||||||
if (isFoldableError(expression)) {
|
|
||||||
return recordEntry(expression, node);
|
|
||||||
}
|
|
||||||
if (!elementAccessExpression.argumentExpression) {
|
|
||||||
return recordEntry(errorSymbol('Expression form not supported', node), node);
|
|
||||||
}
|
|
||||||
const index = this.evaluateNode(elementAccessExpression.argumentExpression);
|
|
||||||
if (isFoldableError(expression)) {
|
|
||||||
return recordEntry(expression, node);
|
|
||||||
}
|
|
||||||
if (this.isFoldable(elementAccessExpression.expression) &&
|
|
||||||
this.isFoldable(elementAccessExpression.argumentExpression))
|
|
||||||
return (<any>expression)[<string|number>index];
|
|
||||||
return recordEntry({__symbolic: 'index', expression, index}, node);
|
|
||||||
}
|
|
||||||
case ts.SyntaxKind.Identifier:
|
|
||||||
const identifier = <ts.Identifier>node;
|
|
||||||
const name = identifier.text;
|
|
||||||
return resolveName(name, preferReference);
|
|
||||||
case ts.SyntaxKind.TypeReference:
|
|
||||||
const typeReferenceNode = <ts.TypeReferenceNode>node;
|
|
||||||
const typeNameNode = typeReferenceNode.typeName;
|
|
||||||
const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) => MetadataValue =
|
|
||||||
node => {
|
|
||||||
if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
|
|
||||||
const qualifiedName = <ts.QualifiedName>node;
|
|
||||||
const left = this.evaluateNode(qualifiedName.left);
|
|
||||||
if (isMetadataModuleReferenceExpression(left)) {
|
|
||||||
return recordEntry(
|
|
||||||
<MetadataImportedSymbolReferenceExpression>{
|
|
||||||
__symbolic: 'reference',
|
|
||||||
module: left.module,
|
|
||||||
name: qualifiedName.right.text
|
|
||||||
},
|
|
||||||
node);
|
|
||||||
}
|
|
||||||
// Record a type reference to a declared type as a select.
|
|
||||||
return {__symbolic: 'select', expression: left, member: qualifiedName.right.text};
|
|
||||||
} else {
|
|
||||||
const identifier = <ts.Identifier>typeNameNode;
|
|
||||||
const symbol = this.symbols.resolve(identifier.text);
|
|
||||||
if (isFoldableError(symbol) || isMetadataSymbolicReferenceExpression(symbol)) {
|
|
||||||
return recordEntry(symbol, node);
|
|
||||||
}
|
|
||||||
return recordEntry(
|
|
||||||
errorSymbol('Could not resolve type', node, {typeName: identifier.text}), node);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const typeReference = getReference(typeNameNode);
|
|
||||||
if (isFoldableError(typeReference)) {
|
|
||||||
return recordEntry(typeReference, node);
|
|
||||||
}
|
|
||||||
if (!isMetadataModuleReferenceExpression(typeReference) &&
|
|
||||||
typeReferenceNode.typeArguments && typeReferenceNode.typeArguments.length) {
|
|
||||||
const args = typeReferenceNode.typeArguments.map(element => this.evaluateNode(element));
|
|
||||||
// TODO: Remove typecast when upgraded to 2.0 as it will be corretly inferred.
|
|
||||||
// Some versions of 1.9 do not infer this correctly.
|
|
||||||
(<MetadataImportedSymbolReferenceExpression>typeReference).arguments = args;
|
|
||||||
}
|
|
||||||
return recordEntry(typeReference, node);
|
|
||||||
case ts.SyntaxKind.UnionType:
|
|
||||||
const unionType = <ts.UnionTypeNode>node;
|
|
||||||
|
|
||||||
// Remove null and undefined from the list of unions.
|
|
||||||
const references = unionType.types
|
|
||||||
.filter(
|
|
||||||
n => n.kind != ts.SyntaxKind.NullKeyword &&
|
|
||||||
n.kind != ts.SyntaxKind.UndefinedKeyword)
|
|
||||||
.map(n => this.evaluateNode(n));
|
|
||||||
|
|
||||||
// The remmaining reference must be the same. If two have type arguments consider them
|
|
||||||
// different even if the type arguments are the same.
|
|
||||||
let candidate: any = null;
|
|
||||||
for (let i = 0; i < references.length; i++) {
|
|
||||||
const reference = references[i];
|
|
||||||
if (isMetadataSymbolicReferenceExpression(reference)) {
|
|
||||||
if (candidate) {
|
|
||||||
if ((reference as any).name == candidate.name &&
|
|
||||||
(reference as any).module == candidate.module && !(reference as any).arguments) {
|
|
||||||
candidate = reference;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
candidate = reference;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return reference;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (candidate) return candidate;
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.NoSubstitutionTemplateLiteral:
|
|
||||||
case ts.SyntaxKind.StringLiteral:
|
|
||||||
case ts.SyntaxKind.TemplateHead:
|
|
||||||
case ts.SyntaxKind.TemplateTail:
|
|
||||||
case ts.SyntaxKind.TemplateMiddle:
|
|
||||||
return (<ts.LiteralLikeNode>node).text;
|
|
||||||
case ts.SyntaxKind.NumericLiteral:
|
|
||||||
return parseFloat((<ts.LiteralExpression>node).text);
|
|
||||||
case ts.SyntaxKind.AnyKeyword:
|
|
||||||
return recordEntry({__symbolic: 'reference', name: 'any'}, node);
|
|
||||||
case ts.SyntaxKind.StringKeyword:
|
|
||||||
return recordEntry({__symbolic: 'reference', name: 'string'}, node);
|
|
||||||
case ts.SyntaxKind.NumberKeyword:
|
|
||||||
return recordEntry({__symbolic: 'reference', name: 'number'}, node);
|
|
||||||
case ts.SyntaxKind.BooleanKeyword:
|
|
||||||
return recordEntry({__symbolic: 'reference', name: 'boolean'}, node);
|
|
||||||
case ts.SyntaxKind.ArrayType:
|
|
||||||
const arrayTypeNode = <ts.ArrayTypeNode>node;
|
|
||||||
return recordEntry(
|
|
||||||
{
|
|
||||||
__symbolic: 'reference',
|
|
||||||
name: 'Array',
|
|
||||||
arguments: [this.evaluateNode(arrayTypeNode.elementType)]
|
|
||||||
},
|
|
||||||
node);
|
|
||||||
case ts.SyntaxKind.NullKeyword:
|
|
||||||
return null;
|
|
||||||
case ts.SyntaxKind.TrueKeyword:
|
|
||||||
return true;
|
|
||||||
case ts.SyntaxKind.FalseKeyword:
|
|
||||||
return false;
|
|
||||||
case ts.SyntaxKind.ParenthesizedExpression:
|
|
||||||
const parenthesizedExpression = <ts.ParenthesizedExpression>node;
|
|
||||||
return this.evaluateNode(parenthesizedExpression.expression);
|
|
||||||
case ts.SyntaxKind.TypeAssertionExpression:
|
|
||||||
const typeAssertion = <ts.TypeAssertion>node;
|
|
||||||
return this.evaluateNode(typeAssertion.expression);
|
|
||||||
case ts.SyntaxKind.PrefixUnaryExpression:
|
|
||||||
const prefixUnaryExpression = <ts.PrefixUnaryExpression>node;
|
|
||||||
const operand = this.evaluateNode(prefixUnaryExpression.operand);
|
|
||||||
if (isDefined(operand) && isPrimitive(operand)) {
|
|
||||||
switch (prefixUnaryExpression.operator) {
|
|
||||||
case ts.SyntaxKind.PlusToken:
|
|
||||||
return +(operand as any);
|
|
||||||
case ts.SyntaxKind.MinusToken:
|
|
||||||
return -(operand as any);
|
|
||||||
case ts.SyntaxKind.TildeToken:
|
|
||||||
return ~(operand as any);
|
|
||||||
case ts.SyntaxKind.ExclamationToken:
|
|
||||||
return !operand;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let operatorText: string;
|
|
||||||
switch (prefixUnaryExpression.operator) {
|
|
||||||
case ts.SyntaxKind.PlusToken:
|
|
||||||
operatorText = '+';
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.MinusToken:
|
|
||||||
operatorText = '-';
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.TildeToken:
|
|
||||||
operatorText = '~';
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.ExclamationToken:
|
|
||||||
operatorText = '!';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return recordEntry({__symbolic: 'pre', operator: operatorText, operand: operand}, node);
|
|
||||||
case ts.SyntaxKind.BinaryExpression:
|
|
||||||
const binaryExpression = <ts.BinaryExpression>node;
|
|
||||||
const left = this.evaluateNode(binaryExpression.left);
|
|
||||||
const right = this.evaluateNode(binaryExpression.right);
|
|
||||||
if (isDefined(left) && isDefined(right)) {
|
|
||||||
if (isPrimitive(left) && isPrimitive(right))
|
|
||||||
switch (binaryExpression.operatorToken.kind) {
|
|
||||||
case ts.SyntaxKind.BarBarToken:
|
|
||||||
return <any>left || <any>right;
|
|
||||||
case ts.SyntaxKind.AmpersandAmpersandToken:
|
|
||||||
return <any>left && <any>right;
|
|
||||||
case ts.SyntaxKind.AmpersandToken:
|
|
||||||
return <any>left & <any>right;
|
|
||||||
case ts.SyntaxKind.BarToken:
|
|
||||||
return <any>left | <any>right;
|
|
||||||
case ts.SyntaxKind.CaretToken:
|
|
||||||
return <any>left ^ <any>right;
|
|
||||||
case ts.SyntaxKind.EqualsEqualsToken:
|
|
||||||
return <any>left == <any>right;
|
|
||||||
case ts.SyntaxKind.ExclamationEqualsToken:
|
|
||||||
return <any>left != <any>right;
|
|
||||||
case ts.SyntaxKind.EqualsEqualsEqualsToken:
|
|
||||||
return <any>left === <any>right;
|
|
||||||
case ts.SyntaxKind.ExclamationEqualsEqualsToken:
|
|
||||||
return <any>left !== <any>right;
|
|
||||||
case ts.SyntaxKind.LessThanToken:
|
|
||||||
return <any>left < <any>right;
|
|
||||||
case ts.SyntaxKind.GreaterThanToken:
|
|
||||||
return <any>left > <any>right;
|
|
||||||
case ts.SyntaxKind.LessThanEqualsToken:
|
|
||||||
return <any>left <= <any>right;
|
|
||||||
case ts.SyntaxKind.GreaterThanEqualsToken:
|
|
||||||
return <any>left >= <any>right;
|
|
||||||
case ts.SyntaxKind.LessThanLessThanToken:
|
|
||||||
return (<any>left) << (<any>right);
|
|
||||||
case ts.SyntaxKind.GreaterThanGreaterThanToken:
|
|
||||||
return <any>left >> <any>right;
|
|
||||||
case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken:
|
|
||||||
return <any>left >>> <any>right;
|
|
||||||
case ts.SyntaxKind.PlusToken:
|
|
||||||
return <any>left + <any>right;
|
|
||||||
case ts.SyntaxKind.MinusToken:
|
|
||||||
return <any>left - <any>right;
|
|
||||||
case ts.SyntaxKind.AsteriskToken:
|
|
||||||
return <any>left * <any>right;
|
|
||||||
case ts.SyntaxKind.SlashToken:
|
|
||||||
return <any>left / <any>right;
|
|
||||||
case ts.SyntaxKind.PercentToken:
|
|
||||||
return <any>left % <any>right;
|
|
||||||
}
|
|
||||||
return recordEntry(
|
|
||||||
{
|
|
||||||
__symbolic: 'binop',
|
|
||||||
operator: binaryExpression.operatorToken.getText(),
|
|
||||||
left: left,
|
|
||||||
right: right
|
|
||||||
},
|
|
||||||
node);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.ConditionalExpression:
|
|
||||||
const conditionalExpression = <ts.ConditionalExpression>node;
|
|
||||||
const condition = this.evaluateNode(conditionalExpression.condition);
|
|
||||||
const thenExpression = this.evaluateNode(conditionalExpression.whenTrue);
|
|
||||||
const elseExpression = this.evaluateNode(conditionalExpression.whenFalse);
|
|
||||||
if (isPrimitive(condition)) {
|
|
||||||
return condition ? thenExpression : elseExpression;
|
|
||||||
}
|
|
||||||
return recordEntry({__symbolic: 'if', condition, thenExpression, elseExpression}, node);
|
|
||||||
case ts.SyntaxKind.FunctionExpression:
|
|
||||||
case ts.SyntaxKind.ArrowFunction:
|
|
||||||
return recordEntry(errorSymbol('Function call not supported', node), node);
|
|
||||||
case ts.SyntaxKind.TaggedTemplateExpression:
|
|
||||||
return recordEntry(
|
|
||||||
errorSymbol('Tagged template expressions are not supported in metadata', node), node);
|
|
||||||
case ts.SyntaxKind.TemplateExpression:
|
|
||||||
const templateExpression = <ts.TemplateExpression>node;
|
|
||||||
if (this.isFoldable(node)) {
|
|
||||||
return templateExpression.templateSpans.reduce(
|
|
||||||
(previous, current) => previous + <string>this.evaluateNode(current.expression) +
|
|
||||||
<string>this.evaluateNode(current.literal),
|
|
||||||
this.evaluateNode(templateExpression.head));
|
|
||||||
} else {
|
|
||||||
return templateExpression.templateSpans.reduce((previous, current) => {
|
|
||||||
const expr = this.evaluateNode(current.expression);
|
|
||||||
const literal = this.evaluateNode(current.literal);
|
|
||||||
if (isFoldableError(expr)) return expr;
|
|
||||||
if (isFoldableError(literal)) return literal;
|
|
||||||
if (typeof previous === 'string' && typeof expr === 'string' &&
|
|
||||||
typeof literal === 'string') {
|
|
||||||
return previous + expr + literal;
|
|
||||||
}
|
|
||||||
let result = expr;
|
|
||||||
if (previous !== '') {
|
|
||||||
result = {__symbolic: 'binop', operator: '+', left: previous, right: expr};
|
|
||||||
}
|
|
||||||
if (literal != '') {
|
|
||||||
result = {__symbolic: 'binop', operator: '+', left: result, right: literal};
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}, this.evaluateNode(templateExpression.head));
|
|
||||||
}
|
|
||||||
case ts.SyntaxKind.AsExpression:
|
|
||||||
const asExpression = <ts.AsExpression>node;
|
|
||||||
return this.evaluateNode(asExpression.expression);
|
|
||||||
case ts.SyntaxKind.ClassExpression:
|
|
||||||
return {__symbolic: 'class'};
|
|
||||||
}
|
|
||||||
return recordEntry(errorSymbol('Expression form not supported', node), node);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function isPropertyAssignment(node: ts.Node): node is ts.PropertyAssignment {
|
|
||||||
return node.kind == ts.SyntaxKind.PropertyAssignment;
|
|
||||||
}
|
|
||||||
|
|
||||||
const empty = ts.createNodeArray<any>();
|
|
||||||
|
|
||||||
function arrayOrEmpty<T extends ts.Node>(v: ts.NodeArray<T>| undefined): ts.NodeArray<T> {
|
|
||||||
return v || empty;
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {BundlePrivateEntry} from './bundler';
|
|
||||||
|
|
||||||
const INDEX_HEADER = `/**
|
|
||||||
* Generated bundle index. Do not edit.
|
|
||||||
*/
|
|
||||||
`;
|
|
||||||
|
|
||||||
type MapEntry = [string, BundlePrivateEntry[]];
|
|
||||||
|
|
||||||
export function privateEntriesToIndex(index: string, privates: BundlePrivateEntry[]): string {
|
|
||||||
const results: string[] = [INDEX_HEADER];
|
|
||||||
|
|
||||||
// Export all of the index symbols.
|
|
||||||
results.push(`export * from '${index}';`, '');
|
|
||||||
|
|
||||||
// Simplify the exports
|
|
||||||
const exports = new Map<string, BundlePrivateEntry[]>();
|
|
||||||
|
|
||||||
for (const entry of privates) {
|
|
||||||
let entries = exports.get(entry.module);
|
|
||||||
if (!entries) {
|
|
||||||
entries = [];
|
|
||||||
exports.set(entry.module, entries);
|
|
||||||
}
|
|
||||||
entries.push(entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const compareEntries = compare((e: BundlePrivateEntry) => e.name);
|
|
||||||
const compareModules = compare((e: MapEntry) => e[0]);
|
|
||||||
const orderedExports =
|
|
||||||
Array.from(exports)
|
|
||||||
.map(([module, entries]) => <MapEntry>[module, entries.sort(compareEntries)])
|
|
||||||
.sort(compareModules);
|
|
||||||
|
|
||||||
for (const [module, entries] of orderedExports) {
|
|
||||||
let symbols = entries.map(e => `${e.name} as ${e.privateName}`);
|
|
||||||
results.push(`export {${symbols}} from '${module}';`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return results.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
function compare<E, T>(select: (e: E) => T): (a: E, b: E) => number {
|
|
||||||
return (a, b) => {
|
|
||||||
const ak = select(a);
|
|
||||||
const bk = select(b);
|
|
||||||
return ak > bk ? 1 : ak < bk ? -1 : 0;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as tsickle from 'tsickle';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {CliOptions} from './cli_options';
|
|
||||||
import {MetadataWriterHost} from './compiler_host';
|
|
||||||
|
|
||||||
import {CodegenExtension, createBundleIndexHost} from './main_no_tsickle';
|
|
||||||
import {check, tsc} from './tsc';
|
|
||||||
import {VinylFile, isVinylFile} from './vinyl_file';
|
|
||||||
|
|
||||||
const TS_EXT = /\.ts$/;
|
|
||||||
|
|
||||||
export function main(
|
|
||||||
project: string | VinylFile, cliOptions: CliOptions, codegen?: CodegenExtension,
|
|
||||||
options?: ts.CompilerOptions): Promise<any> {
|
|
||||||
try {
|
|
||||||
let projectDir = project;
|
|
||||||
// project is vinyl like file object
|
|
||||||
if (isVinylFile(project)) {
|
|
||||||
projectDir = path.dirname(project.path);
|
|
||||||
}
|
|
||||||
// project is path to project file
|
|
||||||
else if (fs.lstatSync(project).isFile()) {
|
|
||||||
projectDir = path.dirname(project);
|
|
||||||
}
|
|
||||||
|
|
||||||
// file names in tsconfig are resolved relative to this absolute path
|
|
||||||
const basePath = path.resolve(process.cwd(), cliOptions.basePath || projectDir);
|
|
||||||
|
|
||||||
// read the configuration options from wherever you store them
|
|
||||||
let {parsed, ngOptions} = tsc.readConfiguration(project, basePath, options);
|
|
||||||
ngOptions.basePath = basePath;
|
|
||||||
let rootFileNames: string[] = parsed.fileNames.slice(0);
|
|
||||||
const createProgram = (host: ts.CompilerHost, oldProgram?: ts.Program) => {
|
|
||||||
return ts.createProgram(rootFileNames.slice(0), parsed.options, host, oldProgram);
|
|
||||||
};
|
|
||||||
const addGeneratedFileName = (genFileName: string) => {
|
|
||||||
if (genFileName.startsWith(basePath) && TS_EXT.exec(genFileName)) {
|
|
||||||
rootFileNames.push(genFileName);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const diagnostics = (parsed.options as any).diagnostics;
|
|
||||||
if (diagnostics) (ts as any).performance.enable();
|
|
||||||
|
|
||||||
let host = ts.createCompilerHost(parsed.options, true);
|
|
||||||
// Make sure we do not `host.realpath()` from TS as we do not want to resolve symlinks.
|
|
||||||
// https://github.com/Microsoft/TypeScript/issues/9552
|
|
||||||
host.realpath = (fileName: string) => fileName;
|
|
||||||
|
|
||||||
// If the compilation is a flat module index then produce the flat module index
|
|
||||||
// metadata and the synthetic flat module index.
|
|
||||||
if (ngOptions.flatModuleOutFile && !ngOptions.skipMetadataEmit) {
|
|
||||||
const {host: bundleHost, indexName, errors} =
|
|
||||||
createBundleIndexHost(ngOptions, rootFileNames, host);
|
|
||||||
if (errors) check(errors);
|
|
||||||
if (indexName) addGeneratedFileName(indexName);
|
|
||||||
host = bundleHost;
|
|
||||||
}
|
|
||||||
|
|
||||||
const tsickleHost: tsickle.TsickleHost = {
|
|
||||||
shouldSkipTsickleProcessing: (fileName) => /\.d\.ts$/.test(fileName),
|
|
||||||
pathToModuleName: (context, importPath) => '',
|
|
||||||
shouldIgnoreWarningsForPath: (filePath) => false,
|
|
||||||
fileNameToModuleId: (fileName) => fileName,
|
|
||||||
googmodule: false,
|
|
||||||
untyped: true,
|
|
||||||
convertIndexImportShorthand: false,
|
|
||||||
transformDecorators: ngOptions.annotationsAs !== 'decorators',
|
|
||||||
transformTypesToClosure: ngOptions.annotateForClosureCompiler,
|
|
||||||
};
|
|
||||||
|
|
||||||
const program = createProgram(host);
|
|
||||||
|
|
||||||
const errors = program.getOptionsDiagnostics();
|
|
||||||
check(errors);
|
|
||||||
|
|
||||||
if (ngOptions.skipTemplateCodegen || !codegen) {
|
|
||||||
codegen = () => Promise.resolve([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (diagnostics) console.time('NG codegen');
|
|
||||||
return codegen(ngOptions, cliOptions, program, host).then((genFiles) => {
|
|
||||||
if (diagnostics) console.timeEnd('NG codegen');
|
|
||||||
|
|
||||||
// Add the generated files to the configuration so they will become part of the program.
|
|
||||||
if (ngOptions.alwaysCompileGeneratedCode) {
|
|
||||||
genFiles.forEach(genFileName => addGeneratedFileName(genFileName));
|
|
||||||
}
|
|
||||||
if (!ngOptions.skipMetadataEmit) {
|
|
||||||
host = new MetadataWriterHost(host, ngOptions, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new program since codegen files were created after making the old program
|
|
||||||
let programWithCodegen = createProgram(host, program);
|
|
||||||
tsc.typeCheck(host, programWithCodegen);
|
|
||||||
|
|
||||||
if (diagnostics) console.time('Emit');
|
|
||||||
const {diagnostics: emitDiags} =
|
|
||||||
tsickle.emitWithTsickle(programWithCodegen, tsickleHost, host, ngOptions);
|
|
||||||
if (diagnostics) console.timeEnd('Emit');
|
|
||||||
check(emitDiags);
|
|
||||||
|
|
||||||
if (diagnostics) {
|
|
||||||
(ts as any).performance.forEachMeasure(
|
|
||||||
(name: string, duration: number) => { console.error(`TS ${name}: ${duration}ms`); });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
return Promise.reject(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CLI entry point
|
|
||||||
if (require.main === module) {
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
let {options, errors} = (ts as any).parseCommandLine(args);
|
|
||||||
check(errors);
|
|
||||||
const project = options.project || '.';
|
|
||||||
// TODO(alexeagle): command line should be TSC-compatible, remove "CliOptions" here
|
|
||||||
const cliOptions = new CliOptions(require('minimist')(args));
|
|
||||||
main(project, cliOptions, undefined, options)
|
|
||||||
.then((exitCode: any) => process.exit(exitCode))
|
|
||||||
.catch((e: any) => {
|
|
||||||
console.error(e.stack);
|
|
||||||
console.error('Compilation failed');
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {CompilerHostAdapter, MetadataBundler} from './bundler';
|
|
||||||
import {CliOptions} from './cli_options';
|
|
||||||
import {createSyntheticIndexHost} from './compiler_host';
|
|
||||||
import {privateEntriesToIndex} from './index_writer';
|
|
||||||
import NgOptions from './options';
|
|
||||||
|
|
||||||
export {UserError} from './tsc';
|
|
||||||
|
|
||||||
export interface CodegenExtension {
|
|
||||||
/**
|
|
||||||
* Returns the generated file names.
|
|
||||||
*/
|
|
||||||
(ngOptions: NgOptions, cliOptions: CliOptions, program: ts.Program,
|
|
||||||
host: ts.CompilerHost): Promise<string[]>;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DTS = /\.d\.ts$/;
|
|
||||||
const JS_EXT = /(\.js|)$/;
|
|
||||||
|
|
||||||
export function createBundleIndexHost<H extends ts.CompilerHost>(
|
|
||||||
ngOptions: NgOptions, rootFiles: string[],
|
|
||||||
host: H): {host: H, indexName?: string, errors?: ts.Diagnostic[]} {
|
|
||||||
const files = rootFiles.filter(f => !DTS.test(f));
|
|
||||||
if (files.length != 1) {
|
|
||||||
return {
|
|
||||||
host,
|
|
||||||
errors: [{
|
|
||||||
file: null as any as ts.SourceFile,
|
|
||||||
start: null as any as number,
|
|
||||||
length: null as any as number,
|
|
||||||
messageText:
|
|
||||||
'Angular compiler option "flatModuleIndex" requires one and only one .ts file in the "files" field.',
|
|
||||||
category: ts.DiagnosticCategory.Error,
|
|
||||||
code: 0
|
|
||||||
}]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
const file = files[0];
|
|
||||||
const indexModule = file.replace(/\.ts$/, '');
|
|
||||||
const bundler =
|
|
||||||
new MetadataBundler(indexModule, ngOptions.flatModuleId, new CompilerHostAdapter(host));
|
|
||||||
const metadataBundle = bundler.getMetadataBundle();
|
|
||||||
const metadata = JSON.stringify(metadataBundle.metadata);
|
|
||||||
const name =
|
|
||||||
path.join(path.dirname(indexModule), ngOptions.flatModuleOutFile !.replace(JS_EXT, '.ts'));
|
|
||||||
const libraryIndex = `./${path.basename(indexModule)}`;
|
|
||||||
const content = privateEntriesToIndex(libraryIndex, metadataBundle.privates);
|
|
||||||
host = createSyntheticIndexHost(host, {name, content, metadata});
|
|
||||||
return {host, indexName: name};
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
interface Options extends ts.CompilerOptions {
|
|
||||||
// Absolute path to a directory where generated file structure is written.
|
|
||||||
// If unspecified, generated files will be written alongside sources.
|
|
||||||
genDir?: string;
|
|
||||||
|
|
||||||
// Path to the directory containing the tsconfig.json file.
|
|
||||||
basePath?: string;
|
|
||||||
|
|
||||||
// Don't produce .metadata.json files (they don't work for bundled emit with --out)
|
|
||||||
skipMetadataEmit?: boolean;
|
|
||||||
|
|
||||||
// Produce an error if the metadata written for a class would produce an error if used.
|
|
||||||
strictMetadataEmit?: boolean;
|
|
||||||
|
|
||||||
// Don't produce .ngfactory.ts or .ngstyle.ts files
|
|
||||||
skipTemplateCodegen?: boolean;
|
|
||||||
|
|
||||||
// Whether to generate a flat module index of the given name and the corresponding
|
|
||||||
// flat module metadata. This option is intended to be used when creating flat
|
|
||||||
// modules similar to how `@angular/core` and `@angular/common` are packaged.
|
|
||||||
// When this option is used the `package.json` for the library should referred to the
|
|
||||||
// generated flat module index instead of the library index file. When using this
|
|
||||||
// option only one .metadata.json file is produced that contains all the metadata
|
|
||||||
// necessary for symbols exported from the library index.
|
|
||||||
// In the generated .ngfactory.ts files flat module index is used to import symbols
|
|
||||||
// includes both the public API from the library index as well as shrowded internal
|
|
||||||
// symbols.
|
|
||||||
// By default the .ts file supplied in the `files` files field is assumed to be
|
|
||||||
// library index. If more than one is specified, uses `libraryIndex` to select the
|
|
||||||
// file to use. If more than on .ts file is supplied and no `libraryIndex` is supplied
|
|
||||||
// an error is produced.
|
|
||||||
// A flat module index .d.ts and .js will be created with the given `flatModuleOutFile`
|
|
||||||
// name in the same location as the library index .d.ts file is emitted.
|
|
||||||
// For example, if a library uses `public_api.ts` file as the library index of the
|
|
||||||
// module the `tsconfig.json` `files` field would be `["public_api.ts"]`. The
|
|
||||||
// `flatModuleOutFile` options could then be set to, for example `"index.js"`, which
|
|
||||||
// produces `index.d.ts` and `index.metadata.json` files. The library's
|
|
||||||
// `package.json`'s `module` field would be `"index.js"` and the `typings` field would
|
|
||||||
// be `"index.d.ts"`.
|
|
||||||
flatModuleOutFile?: string;
|
|
||||||
|
|
||||||
// Preferred module id to use for importing flat module. References generated by `ngc`
|
|
||||||
// will use this module name when importing symbols from the flat module. This is only
|
|
||||||
// meaningful when `flatModuleOutFile` is also supplied. It is otherwise ignored.
|
|
||||||
flatModuleId?: string;
|
|
||||||
|
|
||||||
// Whether to generate code for library code.
|
|
||||||
// If true, produce .ngfactory.ts and .ngstyle.ts files for .d.ts inputs.
|
|
||||||
// Default is true.
|
|
||||||
generateCodeForLibraries?: boolean;
|
|
||||||
|
|
||||||
// Insert JSDoc type annotations needed by Closure Compiler
|
|
||||||
annotateForClosureCompiler?: boolean;
|
|
||||||
|
|
||||||
// Modify how angular annotations are emitted to improve tree-shaking.
|
|
||||||
// Default is static fields.
|
|
||||||
// decorators: Leave the Decorators in-place. This makes compilation faster.
|
|
||||||
// TypeScript will emit calls to the __decorate helper.
|
|
||||||
// `--emitDecoratorMetadata` can be used for runtime reflection.
|
|
||||||
// However, the resulting code will not properly tree-shake.
|
|
||||||
// static fields: Replace decorators with a static field in the class.
|
|
||||||
// Allows advanced tree-shakers like Closure Compiler to remove
|
|
||||||
// unused classes.
|
|
||||||
annotationsAs?: 'decorators'|'static fields';
|
|
||||||
|
|
||||||
// Print extra information while running the compiler
|
|
||||||
trace?: boolean;
|
|
||||||
|
|
||||||
// Whether to enable support for <template> and the template attribute (false by default)
|
|
||||||
enableLegacyTemplate?: boolean;
|
|
||||||
|
|
||||||
// Whether to generate .ngsummary.ts files that allow to use AOTed artifacts
|
|
||||||
// in JIT mode. This is off by default.
|
|
||||||
enableSummariesForJit?: boolean;
|
|
||||||
|
|
||||||
// Whether to compile generated .ngfacgtory.ts files, even when they are no
|
|
||||||
// matched by the `files` / `includes` in the `tsconfig.json`.
|
|
||||||
// This is off by default.
|
|
||||||
alwaysCompileGeneratedCode?: boolean;
|
|
||||||
|
|
||||||
// Whether to remove blank text nodes from compiled templates. It is `true` by default
|
|
||||||
// in Angular 5 and will be re-visited in Angular 6.
|
|
||||||
preserveWhitespaces?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Options;
|
|
@ -1,284 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Metadata Schema
|
|
||||||
|
|
||||||
// If you make a backwards incompatible change to the schema, increment the VERSION number.
|
|
||||||
|
|
||||||
// If you make a backwards compatible change to the metadata (such as adding an option field) then
|
|
||||||
// leave VERSION the same. If possible, as many versions of the metadata that can represent the
|
|
||||||
// semantics of the file in an array. For example, when generating a version 2 file, if version 1
|
|
||||||
// can accurately represent the metadata, generate both version 1 and version 2 in an array.
|
|
||||||
|
|
||||||
export const VERSION = 3;
|
|
||||||
|
|
||||||
export type MetadataEntry = ClassMetadata | InterfaceMetadata | FunctionMetadata | MetadataValue;
|
|
||||||
|
|
||||||
export interface ModuleMetadata {
|
|
||||||
__symbolic: 'module';
|
|
||||||
version: number;
|
|
||||||
exports?: ModuleExportMetadata[];
|
|
||||||
importAs?: string;
|
|
||||||
metadata: {[name: string]: MetadataEntry};
|
|
||||||
origins?: {[name: string]: string};
|
|
||||||
}
|
|
||||||
export function isModuleMetadata(value: any): value is ModuleMetadata {
|
|
||||||
return value && value.__symbolic === 'module';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ModuleExportMetadata {
|
|
||||||
export?: (string|{name: string, as: string})[];
|
|
||||||
from: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ClassMetadata {
|
|
||||||
__symbolic: 'class';
|
|
||||||
extends?: MetadataSymbolicExpression|MetadataError;
|
|
||||||
arity?: number;
|
|
||||||
decorators?: (MetadataSymbolicExpression|MetadataError)[];
|
|
||||||
members?: MetadataMap;
|
|
||||||
statics?: {[name: string]: MetadataValue | FunctionMetadata};
|
|
||||||
}
|
|
||||||
export function isClassMetadata(value: any): value is ClassMetadata {
|
|
||||||
return value && value.__symbolic === 'class';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface InterfaceMetadata { __symbolic: 'interface'; }
|
|
||||||
export function isInterfaceMetadata(value: any): value is InterfaceMetadata {
|
|
||||||
return value && value.__symbolic === 'interface';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataMap { [name: string]: MemberMetadata[]; }
|
|
||||||
|
|
||||||
export interface MemberMetadata {
|
|
||||||
__symbolic: 'constructor'|'method'|'property';
|
|
||||||
decorators?: (MetadataSymbolicExpression|MetadataError)[];
|
|
||||||
}
|
|
||||||
export function isMemberMetadata(value: any): value is MemberMetadata {
|
|
||||||
if (value) {
|
|
||||||
switch (value.__symbolic) {
|
|
||||||
case 'constructor':
|
|
||||||
case 'method':
|
|
||||||
case 'property':
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MethodMetadata extends MemberMetadata {
|
|
||||||
__symbolic: 'constructor'|'method';
|
|
||||||
parameterDecorators?: ((MetadataSymbolicExpression | MetadataError)[]|undefined)[];
|
|
||||||
}
|
|
||||||
export function isMethodMetadata(value: any): value is MethodMetadata {
|
|
||||||
return value && (value.__symbolic === 'constructor' || value.__symbolic === 'method');
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ConstructorMetadata extends MethodMetadata {
|
|
||||||
__symbolic: 'constructor';
|
|
||||||
parameters?: (MetadataSymbolicExpression|MetadataError|null|undefined)[];
|
|
||||||
}
|
|
||||||
export function isConstructorMetadata(value: any): value is ConstructorMetadata {
|
|
||||||
return value && value.__symbolic === 'constructor';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface FunctionMetadata {
|
|
||||||
__symbolic: 'function';
|
|
||||||
parameters: string[];
|
|
||||||
defaults?: MetadataValue[];
|
|
||||||
value: MetadataValue;
|
|
||||||
}
|
|
||||||
export function isFunctionMetadata(value: any): value is FunctionMetadata {
|
|
||||||
return value && value.__symbolic === 'function';
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MetadataValue = string | number | boolean | undefined | null | MetadataObject |
|
|
||||||
MetadataArray | MetadataSymbolicExpression | MetadataError;
|
|
||||||
|
|
||||||
export interface MetadataObject { [name: string]: MetadataValue; }
|
|
||||||
|
|
||||||
export interface MetadataArray { [name: number]: MetadataValue; }
|
|
||||||
|
|
||||||
export interface MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'binary'|'call'|'index'|'new'|'pre'|'reference'|'select'|'spread'|'if';
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicExpression(value: any): value is MetadataSymbolicExpression {
|
|
||||||
if (value) {
|
|
||||||
switch (value.__symbolic) {
|
|
||||||
case 'binary':
|
|
||||||
case 'call':
|
|
||||||
case 'index':
|
|
||||||
case 'new':
|
|
||||||
case 'pre':
|
|
||||||
case 'reference':
|
|
||||||
case 'select':
|
|
||||||
case 'spread':
|
|
||||||
case 'if':
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicBinaryExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'binary';
|
|
||||||
operator: '&&'|'||'|'|'|'^'|'&'|'=='|'!='|'==='|'!=='|'<'|'>'|'<='|'>='|'instanceof'|'in'|'as'|
|
|
||||||
'<<'|'>>'|'>>>'|'+'|'-'|'*'|'/'|'%'|'**';
|
|
||||||
left: MetadataValue;
|
|
||||||
right: MetadataValue;
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicBinaryExpression(value: any):
|
|
||||||
value is MetadataSymbolicBinaryExpression {
|
|
||||||
return value && value.__symbolic === 'binary';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicIndexExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'index';
|
|
||||||
expression: MetadataValue;
|
|
||||||
index: MetadataValue;
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicIndexExpression(value: any):
|
|
||||||
value is MetadataSymbolicIndexExpression {
|
|
||||||
return value && value.__symbolic === 'index';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicCallExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'call'|'new';
|
|
||||||
expression: MetadataValue;
|
|
||||||
arguments?: MetadataValue[];
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicCallExpression(value: any):
|
|
||||||
value is MetadataSymbolicCallExpression {
|
|
||||||
return value && (value.__symbolic === 'call' || value.__symbolic === 'new');
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicPrefixExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'pre';
|
|
||||||
operator: '+'|'-'|'~'|'!';
|
|
||||||
operand: MetadataValue;
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicPrefixExpression(value: any):
|
|
||||||
value is MetadataSymbolicPrefixExpression {
|
|
||||||
return value && value.__symbolic === 'pre';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicIfExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'if';
|
|
||||||
condition: MetadataValue;
|
|
||||||
thenExpression: MetadataValue;
|
|
||||||
elseExpression: MetadataValue;
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicIfExpression(value: any): value is MetadataSymbolicIfExpression {
|
|
||||||
return value && value.__symbolic === 'if';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataGlobalReferenceExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'reference';
|
|
||||||
name: string;
|
|
||||||
arguments?: MetadataValue[];
|
|
||||||
}
|
|
||||||
export function isMetadataGlobalReferenceExpression(value: any):
|
|
||||||
value is MetadataGlobalReferenceExpression {
|
|
||||||
return value && value.name && !value.module && isMetadataSymbolicReferenceExpression(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataModuleReferenceExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'reference';
|
|
||||||
module: string;
|
|
||||||
}
|
|
||||||
export function isMetadataModuleReferenceExpression(value: any):
|
|
||||||
value is MetadataModuleReferenceExpression {
|
|
||||||
return value && value.module && !value.name && !value.default &&
|
|
||||||
isMetadataSymbolicReferenceExpression(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataImportedSymbolReferenceExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'reference';
|
|
||||||
module: string;
|
|
||||||
name: string;
|
|
||||||
arguments?: MetadataValue[];
|
|
||||||
}
|
|
||||||
export function isMetadataImportedSymbolReferenceExpression(value: any):
|
|
||||||
value is MetadataImportedSymbolReferenceExpression {
|
|
||||||
return value && value.module && !!value.name && isMetadataSymbolicReferenceExpression(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataImportedDefaultReferenceExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'reference';
|
|
||||||
module: string;
|
|
||||||
default:
|
|
||||||
boolean;
|
|
||||||
arguments?: MetadataValue[];
|
|
||||||
}
|
|
||||||
export function isMetadataImportDefaultReference(value: any):
|
|
||||||
value is MetadataImportedDefaultReferenceExpression {
|
|
||||||
return value.module && value.default && isMetadataSymbolicReferenceExpression(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
export type MetadataSymbolicReferenceExpression = MetadataGlobalReferenceExpression |
|
|
||||||
MetadataModuleReferenceExpression | MetadataImportedSymbolReferenceExpression |
|
|
||||||
MetadataImportedDefaultReferenceExpression;
|
|
||||||
export function isMetadataSymbolicReferenceExpression(value: any):
|
|
||||||
value is MetadataSymbolicReferenceExpression {
|
|
||||||
return value && value.__symbolic === 'reference';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicSelectExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'select';
|
|
||||||
expression: MetadataValue;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicSelectExpression(value: any):
|
|
||||||
value is MetadataSymbolicSelectExpression {
|
|
||||||
return value && value.__symbolic === 'select';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataSymbolicSpreadExpression extends MetadataSymbolicExpression {
|
|
||||||
__symbolic: 'spread';
|
|
||||||
expression: MetadataValue;
|
|
||||||
}
|
|
||||||
export function isMetadataSymbolicSpreadExpression(value: any):
|
|
||||||
value is MetadataSymbolicSpreadExpression {
|
|
||||||
return value && value.__symbolic === 'spread';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface MetadataError {
|
|
||||||
__symbolic: 'error';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This message should be short and relatively discriptive and should be fixed once it is created.
|
|
||||||
* If the reader doesn't recognize the message, it will display the message unmodified. If the
|
|
||||||
* reader recognizes the error message is it free to use substitute message the is more
|
|
||||||
* descriptive and/or localized.
|
|
||||||
*/
|
|
||||||
message: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The line number of the error in the .ts file the metadata was created for.
|
|
||||||
*/
|
|
||||||
line?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of utf8 code-units from the beginning of the file of the error.
|
|
||||||
*/
|
|
||||||
character?: number;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The module of the error (only used in bundled metadata)
|
|
||||||
*/
|
|
||||||
module?: string;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Context information that can be used to generate a more descriptive error message. The content
|
|
||||||
* of the context is dependent on the error message.
|
|
||||||
*/
|
|
||||||
context?: {[name: string]: string};
|
|
||||||
}
|
|
||||||
export function isMetadataError(value: any): value is MetadataError {
|
|
||||||
return value && value.__symbolic === 'error';
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {MetadataSymbolicReferenceExpression, MetadataValue} from './schema';
|
|
||||||
|
|
||||||
export class Symbols {
|
|
||||||
private _symbols: Map<string, MetadataValue>;
|
|
||||||
private references = new Map<string, MetadataSymbolicReferenceExpression>();
|
|
||||||
|
|
||||||
constructor(private sourceFile: ts.SourceFile) {}
|
|
||||||
|
|
||||||
resolve(name: string, preferReference?: boolean): MetadataValue|undefined {
|
|
||||||
return (preferReference && this.references.get(name)) || this.symbols.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
define(name: string, value: MetadataValue) { this.symbols.set(name, value); }
|
|
||||||
defineReference(name: string, value: MetadataSymbolicReferenceExpression) {
|
|
||||||
this.references.set(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
has(name: string): boolean { return this.symbols.has(name); }
|
|
||||||
|
|
||||||
private get symbols(): Map<string, MetadataValue> {
|
|
||||||
let result = this._symbols;
|
|
||||||
if (!result) {
|
|
||||||
result = this._symbols = new Map<string, MetadataValue>();
|
|
||||||
populateBuiltins(result);
|
|
||||||
this.buildImports();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
private buildImports(): void {
|
|
||||||
const symbols = this._symbols;
|
|
||||||
// Collect the imported symbols into this.symbols
|
|
||||||
const stripQuotes = (s: string) => s.replace(/^['"]|['"]$/g, '');
|
|
||||||
const visit = (node: ts.Node) => {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.ImportEqualsDeclaration:
|
|
||||||
const importEqualsDeclaration = <ts.ImportEqualsDeclaration>node;
|
|
||||||
if (importEqualsDeclaration.moduleReference.kind ===
|
|
||||||
ts.SyntaxKind.ExternalModuleReference) {
|
|
||||||
const externalReference =
|
|
||||||
<ts.ExternalModuleReference>importEqualsDeclaration.moduleReference;
|
|
||||||
if (externalReference.expression) {
|
|
||||||
// An `import <identifier> = require(<module-specifier>);
|
|
||||||
if (!externalReference.expression.parent) {
|
|
||||||
// The `parent` field of a node is set by the TypeScript binder (run as
|
|
||||||
// part of the type checker). Setting it here allows us to call `getText()`
|
|
||||||
// even if the `SourceFile` was not type checked (which looks for `SourceFile`
|
|
||||||
// in the parent chain). This doesn't damage the node as the binder unconditionally
|
|
||||||
// sets the parent.
|
|
||||||
externalReference.expression.parent = externalReference;
|
|
||||||
externalReference.parent = this.sourceFile as any;
|
|
||||||
}
|
|
||||||
const from = stripQuotes(externalReference.expression.getText());
|
|
||||||
symbols.set(
|
|
||||||
importEqualsDeclaration.name.text, {__symbolic: 'reference', module: from});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
symbols.set(
|
|
||||||
importEqualsDeclaration.name.text,
|
|
||||||
{__symbolic: 'error', message: `Unsupported import syntax`});
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.ImportDeclaration:
|
|
||||||
const importDecl = <ts.ImportDeclaration>node;
|
|
||||||
if (!importDecl.importClause) {
|
|
||||||
// An `import <module-specifier>` clause which does not bring symbols into scope.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (!importDecl.moduleSpecifier.parent) {
|
|
||||||
// See note above in the `ImportEqualDeclaration` case.
|
|
||||||
importDecl.moduleSpecifier.parent = importDecl;
|
|
||||||
importDecl.parent = this.sourceFile;
|
|
||||||
}
|
|
||||||
const from = stripQuotes(importDecl.moduleSpecifier.getText());
|
|
||||||
if (importDecl.importClause.name) {
|
|
||||||
// An `import <identifier> form <module-specifier>` clause. Record the defualt symbol.
|
|
||||||
symbols.set(
|
|
||||||
importDecl.importClause.name.text,
|
|
||||||
{__symbolic: 'reference', module: from, default: true});
|
|
||||||
}
|
|
||||||
const bindings = importDecl.importClause.namedBindings;
|
|
||||||
if (bindings) {
|
|
||||||
switch (bindings.kind) {
|
|
||||||
case ts.SyntaxKind.NamedImports:
|
|
||||||
// An `import { [<identifier> [, <identifier>] } from <module-specifier>` clause
|
|
||||||
for (const binding of (<ts.NamedImports>bindings).elements) {
|
|
||||||
symbols.set(binding.name.text, {
|
|
||||||
__symbolic: 'reference',
|
|
||||||
module: from,
|
|
||||||
name: binding.propertyName ? binding.propertyName.text : binding.name.text
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ts.SyntaxKind.NamespaceImport:
|
|
||||||
// An `input * as <identifier> from <module-specifier>` clause.
|
|
||||||
symbols.set(
|
|
||||||
(<ts.NamespaceImport>bindings).name.text,
|
|
||||||
{__symbolic: 'reference', module: from});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ts.forEachChild(node, visit);
|
|
||||||
};
|
|
||||||
if (this.sourceFile) {
|
|
||||||
ts.forEachChild(this.sourceFile, visit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function populateBuiltins(symbols: Map<string, MetadataValue>) {
|
|
||||||
// From lib.core.d.ts (all "define const")
|
|
||||||
['Object', 'Function', 'String', 'Number', 'Array', 'Boolean', 'Map', 'NaN', 'Infinity', 'Math',
|
|
||||||
'Date', 'RegExp', 'Error', 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError',
|
|
||||||
'TypeError', 'URIError', 'JSON', 'ArrayBuffer', 'DataView', 'Int8Array', 'Uint8Array',
|
|
||||||
'Uint8ClampedArray', 'Uint16Array', 'Int16Array', 'Int32Array', 'Uint32Array', 'Float32Array',
|
|
||||||
'Float64Array']
|
|
||||||
.forEach(name => symbols.set(name, {__symbolic: 'reference', name}));
|
|
||||||
}
|
|
@ -1,182 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {existsSync} from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import AngularCompilerOptions from './options';
|
|
||||||
import {VinylFile, isVinylFile} from './vinyl_file';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Our interface to the TypeScript standard compiler.
|
|
||||||
* If you write an Angular compiler plugin for another build tool,
|
|
||||||
* you should implement a similar interface.
|
|
||||||
*/
|
|
||||||
export interface CompilerInterface {
|
|
||||||
readConfiguration(
|
|
||||||
project: string|VinylFile, basePath: string, existingOptions?: ts.CompilerOptions):
|
|
||||||
{parsed: ts.ParsedCommandLine, ngOptions: AngularCompilerOptions};
|
|
||||||
typeCheck(compilerHost: ts.CompilerHost, program: ts.Program): void;
|
|
||||||
emit(program: ts.Program): number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class UserError extends Error {
|
|
||||||
private _nativeError: Error;
|
|
||||||
|
|
||||||
constructor(message: string) {
|
|
||||||
super(message);
|
|
||||||
// Required for TS 2.1, see
|
|
||||||
// https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
||||||
(Object as any).setPrototypeOf(this, UserError.prototype);
|
|
||||||
|
|
||||||
const nativeError = new Error(message) as any as Error;
|
|
||||||
this._nativeError = nativeError;
|
|
||||||
}
|
|
||||||
|
|
||||||
get message() { return this._nativeError.message; }
|
|
||||||
set message(message) {
|
|
||||||
if (this._nativeError) this._nativeError.message = message;
|
|
||||||
}
|
|
||||||
get name() { return this._nativeError.name; }
|
|
||||||
set name(name) {
|
|
||||||
if (this._nativeError) this._nativeError.name = name;
|
|
||||||
}
|
|
||||||
get stack() { return (this._nativeError as any).stack; }
|
|
||||||
set stack(value) {
|
|
||||||
if (this._nativeError) (this._nativeError as any).stack = value;
|
|
||||||
}
|
|
||||||
toString() { return this._nativeError.toString(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEBUG = false;
|
|
||||||
|
|
||||||
function debug(msg: string, ...o: any[]) {
|
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
if (DEBUG) console.log(msg, ...o);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function formatDiagnostics(diags: ts.Diagnostic[]): string {
|
|
||||||
return diags
|
|
||||||
.map((d) => {
|
|
||||||
let res = ts.DiagnosticCategory[d.category];
|
|
||||||
if (d.file) {
|
|
||||||
res += ' at ' + d.file.fileName + ':';
|
|
||||||
if (d.start !== undefined) {
|
|
||||||
const {line, character} = d.file.getLineAndCharacterOfPosition(d.start);
|
|
||||||
res += (line + 1) + ':' + (character + 1) + ':';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res += ' ' + ts.flattenDiagnosticMessageText(d.messageText, '\n');
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.join('\n');
|
|
||||||
}
|
|
||||||
|
|
||||||
export function check(diags: ts.Diagnostic[] | undefined) {
|
|
||||||
if (diags && diags.length && diags[0]) {
|
|
||||||
throw new UserError(formatDiagnostics(diags));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateAngularCompilerOptions(options: AngularCompilerOptions): ts.Diagnostic[]|
|
|
||||||
undefined {
|
|
||||||
if (options.annotationsAs) {
|
|
||||||
switch (options.annotationsAs) {
|
|
||||||
case 'decorators':
|
|
||||||
case 'static fields':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return [{
|
|
||||||
file: null as any as ts.SourceFile,
|
|
||||||
start: null as any as number,
|
|
||||||
length: null as any as number,
|
|
||||||
messageText:
|
|
||||||
'Angular compiler options "annotationsAs" only supports "static fields" and "decorators"',
|
|
||||||
category: ts.DiagnosticCategory.Error,
|
|
||||||
code: 0
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class Tsc implements CompilerInterface {
|
|
||||||
private parseConfigHost: ts.ParseConfigHost;
|
|
||||||
|
|
||||||
constructor(private readFile = ts.sys.readFile, private readDirectory = ts.sys.readDirectory) {
|
|
||||||
this.parseConfigHost = {
|
|
||||||
useCaseSensitiveFileNames: true,
|
|
||||||
fileExists: existsSync,
|
|
||||||
readDirectory: this.readDirectory,
|
|
||||||
readFile: ts.sys.readFile
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
readConfiguration(
|
|
||||||
project: string|VinylFile, basePath: string, existingOptions?: ts.CompilerOptions) {
|
|
||||||
// Allow a directory containing tsconfig.json as the project value
|
|
||||||
// Note, TS@next returns an empty array, while earlier versions throw
|
|
||||||
try {
|
|
||||||
if (!isVinylFile(project) && this.readDirectory(project).length > 0) {
|
|
||||||
project = path.join(project, 'tsconfig.json');
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// Was not a directory, continue on assuming it's a file
|
|
||||||
}
|
|
||||||
|
|
||||||
let {config, error} = (() => {
|
|
||||||
// project is vinyl like file object
|
|
||||||
if (isVinylFile(project)) {
|
|
||||||
return {config: JSON.parse(project.contents.toString()), error: null};
|
|
||||||
}
|
|
||||||
// project is path to project file
|
|
||||||
else {
|
|
||||||
return ts.readConfigFile(project, this.readFile);
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
check([error !]);
|
|
||||||
|
|
||||||
const parsed =
|
|
||||||
ts.parseJsonConfigFileContent(config, this.parseConfigHost, basePath, existingOptions);
|
|
||||||
|
|
||||||
check(parsed.errors);
|
|
||||||
|
|
||||||
// Default codegen goes to the current directory
|
|
||||||
// Parsed options are already converted to absolute paths
|
|
||||||
const ngOptions = config.angularCompilerOptions || {};
|
|
||||||
ngOptions.genDir = path.join(basePath, ngOptions.genDir || '.');
|
|
||||||
for (const key of Object.keys(parsed.options)) {
|
|
||||||
ngOptions[key] = parsed.options[key];
|
|
||||||
}
|
|
||||||
check(validateAngularCompilerOptions(ngOptions));
|
|
||||||
|
|
||||||
return {parsed, ngOptions};
|
|
||||||
}
|
|
||||||
|
|
||||||
typeCheck(compilerHost: ts.CompilerHost, program: ts.Program): void {
|
|
||||||
debug('Checking global diagnostics...');
|
|
||||||
check(program.getGlobalDiagnostics());
|
|
||||||
|
|
||||||
const diagnostics: ts.Diagnostic[] = [];
|
|
||||||
debug('Type checking...');
|
|
||||||
|
|
||||||
for (const sf of program.getSourceFiles()) {
|
|
||||||
diagnostics.push(...ts.getPreEmitDiagnostics(program, sf));
|
|
||||||
}
|
|
||||||
check(diagnostics);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(program: ts.Program): number {
|
|
||||||
debug('Emitting outputs...');
|
|
||||||
const emitResult = program.emit();
|
|
||||||
const diagnostics: ts.Diagnostic[] = [];
|
|
||||||
diagnostics.push(...emitResult.diagnostics);
|
|
||||||
return emitResult.emitSkipped ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
export const tsc: CompilerInterface = new Tsc();
|
|
@ -1,18 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
export interface VinylFile extends Object {
|
|
||||||
// Absolute path to the virtual file
|
|
||||||
path: string;
|
|
||||||
|
|
||||||
// Content of the virtual file
|
|
||||||
contents: Buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isVinylFile(obj: any): obj is VinylFile {
|
|
||||||
return (typeof obj === 'object') && ('path' in obj) && ('contents' in obj);
|
|
||||||
}
|
|
@ -1,280 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as path from 'path';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {MetadataBundler, MetadataBundlerHost} from '../src/bundler';
|
|
||||||
import {MetadataCollector} from '../src/collector';
|
|
||||||
import {ClassMetadata, MetadataGlobalReferenceExpression, ModuleMetadata} from '../src/schema';
|
|
||||||
|
|
||||||
import {Directory, open} from './typescript.mocks';
|
|
||||||
|
|
||||||
describe('metadata bundler', () => {
|
|
||||||
|
|
||||||
it('should be able to bundle a simple library', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', SIMPLE_LIBRARY);
|
|
||||||
const bundler = new MetadataBundler('/lib/index', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
expect(Object.keys(result.metadata.metadata).sort()).toEqual([
|
|
||||||
'ONE_CLASSES', 'One', 'OneMore', 'TWO_CLASSES', 'Two', 'TwoMore', 'ɵa', 'ɵb'
|
|
||||||
]);
|
|
||||||
|
|
||||||
const originalOne = './src/one';
|
|
||||||
const originalTwo = './src/two/index';
|
|
||||||
expect(Object.keys(result.metadata.origins)
|
|
||||||
.sort()
|
|
||||||
.map(name => ({name, value: result.metadata.origins ![name]})))
|
|
||||||
.toEqual([
|
|
||||||
{name: 'ONE_CLASSES', value: originalOne}, {name: 'One', value: originalOne},
|
|
||||||
{name: 'OneMore', value: originalOne}, {name: 'TWO_CLASSES', value: originalTwo},
|
|
||||||
{name: 'Two', value: originalTwo}, {name: 'TwoMore', value: originalTwo},
|
|
||||||
{name: 'ɵa', value: originalOne}, {name: 'ɵb', value: originalTwo}
|
|
||||||
]);
|
|
||||||
expect(result.privates).toEqual([
|
|
||||||
{privateName: 'ɵa', name: 'PrivateOne', module: originalOne},
|
|
||||||
{privateName: 'ɵb', name: 'PrivateTwo', module: originalTwo}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to bundle an oddly constructed library', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'lib': {
|
|
||||||
'index.ts': `
|
|
||||||
export * from './src/index';
|
|
||||||
`,
|
|
||||||
'src': {
|
|
||||||
'index.ts': `
|
|
||||||
export {One, OneMore, ONE_CLASSES} from './one';
|
|
||||||
export {Two, TwoMore, TWO_CLASSES} from './two/index';
|
|
||||||
`,
|
|
||||||
'one.ts': `
|
|
||||||
class One {}
|
|
||||||
class OneMore extends One {}
|
|
||||||
class PrivateOne {}
|
|
||||||
const ONE_CLASSES = [One, OneMore, PrivateOne];
|
|
||||||
export {One, OneMore, PrivateOne, ONE_CLASSES};
|
|
||||||
`,
|
|
||||||
'two': {
|
|
||||||
'index.ts': `
|
|
||||||
class Two {}
|
|
||||||
class TwoMore extends Two {}
|
|
||||||
class PrivateTwo {}
|
|
||||||
const TWO_CLASSES = [Two, TwoMore, PrivateTwo];
|
|
||||||
export {Two, TwoMore, PrivateTwo, TWO_CLASSES};
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const bundler = new MetadataBundler('/lib/index', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
expect(Object.keys(result.metadata.metadata).sort()).toEqual([
|
|
||||||
'ONE_CLASSES', 'One', 'OneMore', 'TWO_CLASSES', 'Two', 'TwoMore', 'ɵa', 'ɵb'
|
|
||||||
]);
|
|
||||||
expect(result.privates).toEqual([
|
|
||||||
{privateName: 'ɵa', name: 'PrivateOne', module: './src/one'},
|
|
||||||
{privateName: 'ɵb', name: 'PrivateTwo', module: './src/two/index'}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not output windows paths in metadata', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'index.ts': `
|
|
||||||
export * from './exports/test';
|
|
||||||
`,
|
|
||||||
'exports': {'test.ts': `export class TestExport {}`}
|
|
||||||
});
|
|
||||||
const bundler = new MetadataBundler('/index', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
|
|
||||||
expect(result.metadata.origins).toEqual({'TestExport': './exports/test'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should convert re-exported to the export', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'index.ts': `
|
|
||||||
export * from './bar';
|
|
||||||
export * from './foo';
|
|
||||||
`,
|
|
||||||
'bar.ts': `
|
|
||||||
import {Foo} from './foo';
|
|
||||||
export class Bar extends Foo {
|
|
||||||
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
'foo.ts': `
|
|
||||||
export {Foo} from 'foo';
|
|
||||||
`
|
|
||||||
});
|
|
||||||
const bundler = new MetadataBundler('/index', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
// Expect the extends reference to refer to the imported module
|
|
||||||
expect((result.metadata.metadata as any).Bar.extends.module).toEqual('foo');
|
|
||||||
expect(result.privates).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should treat import then export as a simple export', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'index.ts': `
|
|
||||||
export * from './a';
|
|
||||||
export * from './c';
|
|
||||||
`,
|
|
||||||
'a.ts': `
|
|
||||||
import { B } from './b';
|
|
||||||
export { B };
|
|
||||||
`,
|
|
||||||
'b.ts': `
|
|
||||||
export class B { }
|
|
||||||
`,
|
|
||||||
'c.ts': `
|
|
||||||
import { B } from './b';
|
|
||||||
export class C extends B { }
|
|
||||||
`
|
|
||||||
});
|
|
||||||
const bundler = new MetadataBundler('/index', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
expect(Object.keys(result.metadata.metadata).sort()).toEqual(['B', 'C']);
|
|
||||||
expect(result.privates).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to bundle a private from a un-exported module', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'index.ts': `
|
|
||||||
export * from './foo';
|
|
||||||
`,
|
|
||||||
'foo.ts': `
|
|
||||||
import {Bar} from './bar';
|
|
||||||
export class Foo extends Bar {
|
|
||||||
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
'bar.ts': `
|
|
||||||
export class Bar {}
|
|
||||||
`
|
|
||||||
});
|
|
||||||
const bundler = new MetadataBundler('/index', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
expect(Object.keys(result.metadata.metadata).sort()).toEqual(['Foo', 'ɵa']);
|
|
||||||
expect(result.privates).toEqual([{privateName: 'ɵa', name: 'Bar', module: './bar'}]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to bundle a library with re-exported symbols', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'public-api.ts': `
|
|
||||||
export * from './src/core';
|
|
||||||
export * from './src/externals';
|
|
||||||
`,
|
|
||||||
'src': {
|
|
||||||
'core.ts': `
|
|
||||||
export class A {}
|
|
||||||
export class B extends A {}
|
|
||||||
`,
|
|
||||||
'externals.ts': `
|
|
||||||
export {E, F, G} from 'external_one';
|
|
||||||
export * from 'external_two';
|
|
||||||
`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundler = new MetadataBundler('/public-api', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
expect(result.metadata.exports).toEqual([
|
|
||||||
{from: 'external_two'}, {
|
|
||||||
export: [{name: 'E', as: 'E'}, {name: 'F', as: 'F'}, {name: 'G', as: 'G'}],
|
|
||||||
from: 'external_one'
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
expect(result.metadata.origins !['E']).toBeUndefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to de-duplicate symbols of re-exported modules', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', {
|
|
||||||
'public-api.ts': `
|
|
||||||
export {A as A2, A, B as B1, B as B2} from './src/core';
|
|
||||||
export {A as A3} from './src/alternate';
|
|
||||||
`,
|
|
||||||
'src': {
|
|
||||||
'core.ts': `
|
|
||||||
export class A {}
|
|
||||||
export class B {}
|
|
||||||
`,
|
|
||||||
'alternate.ts': `
|
|
||||||
export class A {}
|
|
||||||
`,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const bundler = new MetadataBundler('/public-api', undefined, host);
|
|
||||||
const result = bundler.getMetadataBundle();
|
|
||||||
const {A, A2, A3, B1, B2} = result.metadata.metadata as{
|
|
||||||
A: ClassMetadata,
|
|
||||||
A2: MetadataGlobalReferenceExpression,
|
|
||||||
A3: ClassMetadata,
|
|
||||||
B1: ClassMetadata,
|
|
||||||
B2: MetadataGlobalReferenceExpression
|
|
||||||
};
|
|
||||||
expect(A.__symbolic).toEqual('class');
|
|
||||||
expect(A2.__symbolic).toEqual('reference');
|
|
||||||
expect(A2.name).toEqual('A');
|
|
||||||
expect(A3.__symbolic).toEqual('class');
|
|
||||||
expect(B1.__symbolic).toEqual('class');
|
|
||||||
expect(B2.__symbolic).toEqual('reference');
|
|
||||||
expect(B2.name).toEqual('B1');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
export class MockStringBundlerHost implements MetadataBundlerHost {
|
|
||||||
collector = new MetadataCollector();
|
|
||||||
|
|
||||||
constructor(private dirName: string, private directory: Directory) {}
|
|
||||||
|
|
||||||
getMetadataFor(moduleName: string): ModuleMetadata|undefined {
|
|
||||||
const fileName = path.join(this.dirName, moduleName) + '.ts';
|
|
||||||
const text = open(this.directory, fileName);
|
|
||||||
if (typeof text == 'string') {
|
|
||||||
const sourceFile = ts.createSourceFile(
|
|
||||||
fileName, text, ts.ScriptTarget.Latest, /* setParent */ true, ts.ScriptKind.TS);
|
|
||||||
const diagnostics: ts.Diagnostic[] = (sourceFile as any).parseDiagnostics;
|
|
||||||
if (diagnostics && diagnostics.length) {
|
|
||||||
throw Error('Unexpected syntax error in test');
|
|
||||||
}
|
|
||||||
const result = this.collector.getMetadata(sourceFile);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export const SIMPLE_LIBRARY = {
|
|
||||||
'lib': {
|
|
||||||
'index.ts': `
|
|
||||||
export * from './src/index';
|
|
||||||
`,
|
|
||||||
'src': {
|
|
||||||
'index.ts': `
|
|
||||||
export {One, OneMore, ONE_CLASSES} from './one';
|
|
||||||
export {Two, TwoMore, TWO_CLASSES} from './two/index';
|
|
||||||
`,
|
|
||||||
'one.ts': `
|
|
||||||
export class One {}
|
|
||||||
export class OneMore extends One {}
|
|
||||||
export class PrivateOne {}
|
|
||||||
export const ONE_CLASSES = [One, OneMore, PrivateOne];
|
|
||||||
`,
|
|
||||||
'two': {
|
|
||||||
'index.ts': `
|
|
||||||
export class Two {}
|
|
||||||
export class TwoMore extends Two {}
|
|
||||||
export class PrivateTwo {}
|
|
||||||
export const TWO_CLASSES = [Two, TwoMore, PrivateTwo];
|
|
||||||
`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
File diff suppressed because it is too large
Load Diff
@ -1,373 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {Evaluator} from '../src/evaluator';
|
|
||||||
import {Symbols} from '../src/symbols';
|
|
||||||
|
|
||||||
import {Directory, Host, expectNoDiagnostics, findVar, findVarInitializer} from './typescript.mocks';
|
|
||||||
|
|
||||||
describe('Evaluator', () => {
|
|
||||||
const documentRegistry = ts.createDocumentRegistry();
|
|
||||||
let host: ts.LanguageServiceHost;
|
|
||||||
let service: ts.LanguageService;
|
|
||||||
let program: ts.Program;
|
|
||||||
let typeChecker: ts.TypeChecker;
|
|
||||||
let symbols: Symbols;
|
|
||||||
let evaluator: Evaluator;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
host = new Host(FILES, [
|
|
||||||
'expressions.ts', 'consts.ts', 'const_expr.ts', 'forwardRef.ts', 'classes.ts',
|
|
||||||
'newExpression.ts', 'errors.ts', 'declared.ts'
|
|
||||||
]);
|
|
||||||
service = ts.createLanguageService(host, documentRegistry);
|
|
||||||
program = service.getProgram();
|
|
||||||
typeChecker = program.getTypeChecker();
|
|
||||||
symbols = new Symbols(null as any as ts.SourceFile);
|
|
||||||
evaluator = new Evaluator(symbols, new Map());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not have typescript errors in test data', () => {
|
|
||||||
expectNoDiagnostics(service.getCompilerOptionsDiagnostics());
|
|
||||||
for (const sourceFile of program.getSourceFiles()) {
|
|
||||||
expectNoDiagnostics(service.getSyntacticDiagnostics(sourceFile.fileName));
|
|
||||||
if (sourceFile.fileName != 'errors.ts') {
|
|
||||||
// Skip errors.ts because we it has intentional semantic errors that we are testing for.
|
|
||||||
expectNoDiagnostics(service.getSemanticDiagnostics(sourceFile.fileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to fold literal expressions', () => {
|
|
||||||
const consts = program.getSourceFile('consts.ts');
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(consts, 'someName'))).toBeTruthy();
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(consts, 'someBool'))).toBeTruthy();
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(consts, 'one'))).toBeTruthy();
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(consts, 'two'))).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to fold expressions with foldable references', () => {
|
|
||||||
const expressions = program.getSourceFile('expressions.ts');
|
|
||||||
symbols.define('someName', 'some-name');
|
|
||||||
symbols.define('someBool', true);
|
|
||||||
symbols.define('one', 1);
|
|
||||||
symbols.define('two', 2);
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(expressions, 'three'))).toBeTruthy();
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(expressions, 'four'))).toBeTruthy();
|
|
||||||
symbols.define('three', 3);
|
|
||||||
symbols.define('four', 4);
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(expressions, 'obj'))).toBeTruthy();
|
|
||||||
expect(evaluator.isFoldable(findVarInitializer(expressions, 'arr'))).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to evaluate literal expressions', () => {
|
|
||||||
const consts = program.getSourceFile('consts.ts');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(consts, 'someName'))).toBe('some-name');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(consts, 'someBool'))).toBe(true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(consts, 'one'))).toBe(1);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(consts, 'two'))).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to evaluate expressions', () => {
|
|
||||||
const expressions = program.getSourceFile('expressions.ts');
|
|
||||||
symbols.define('someName', 'some-name');
|
|
||||||
symbols.define('someBool', true);
|
|
||||||
symbols.define('one', 1);
|
|
||||||
symbols.define('two', 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'three'))).toBe(3);
|
|
||||||
symbols.define('three', 3);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'four'))).toBe(4);
|
|
||||||
symbols.define('four', 4);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'obj')))
|
|
||||||
.toEqual({one: 1, two: 2, three: 3, four: 4});
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'arr'))).toEqual([1, 2, 3, 4]);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bTrue'))).toEqual(true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bFalse'))).toEqual(false);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bAnd'))).toEqual(true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bOr'))).toEqual(true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'nDiv'))).toEqual(2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'nMod'))).toEqual(1);
|
|
||||||
|
|
||||||
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLOr'))).toEqual(false || true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLAnd'))).toEqual(true && true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bBOr'))).toEqual(0x11 | 0x22);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bBAnd'))).toEqual(0x11 & 0x03);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bXor'))).toEqual(0x11 ^ 0x21);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bEqual')))
|
|
||||||
.toEqual(1 == <any>'1');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bNotEqual')))
|
|
||||||
.toEqual(1 != <any>'1');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bIdentical')))
|
|
||||||
.toEqual(1 === <any>'1');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bNotIdentical')))
|
|
||||||
.toEqual(1 !== <any>'1');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLessThan'))).toEqual(1 < 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bGreaterThan'))).toEqual(1 > 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bLessThanEqual')))
|
|
||||||
.toEqual(1 <= 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bGreaterThanEqual')))
|
|
||||||
.toEqual(1 >= 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftLeft'))).toEqual(1 << 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRight'))).toEqual(-1 >> 2);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'bShiftRightU')))
|
|
||||||
.toEqual(-1 >>> 2);
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should report recursive references as symbolic', () => {
|
|
||||||
const expressions = program.getSourceFile('expressions.ts');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveA')))
|
|
||||||
.toEqual({__symbolic: 'reference', name: 'recursiveB'});
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(expressions, 'recursiveB')))
|
|
||||||
.toEqual({__symbolic: 'reference', name: 'recursiveA'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should correctly handle special cases for CONST_EXPR', () => {
|
|
||||||
const const_expr = program.getSourceFile('const_expr.ts');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bTrue'))).toEqual(true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(const_expr, 'bFalse'))).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should resolve a forwardRef', () => {
|
|
||||||
const forwardRef = program.getSourceFile('forwardRef.ts');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bTrue'))).toEqual(true);
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(forwardRef, 'bFalse'))).toEqual(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return new expressions', () => {
|
|
||||||
symbols.define('Value', {__symbolic: 'reference', module: './classes', name: 'Value'});
|
|
||||||
evaluator = new Evaluator(symbols, new Map());
|
|
||||||
const newExpression = program.getSourceFile('newExpression.ts');
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'someValue'))).toEqual({
|
|
||||||
__symbolic: 'new',
|
|
||||||
expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
|
|
||||||
arguments: ['name', 12]
|
|
||||||
});
|
|
||||||
expect(evaluator.evaluateNode(findVarInitializer(newExpression, 'complex'))).toEqual({
|
|
||||||
__symbolic: 'new',
|
|
||||||
expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
|
|
||||||
arguments: ['name', 12]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support referene to a declared module type', () => {
|
|
||||||
const declared = program.getSourceFile('declared.ts');
|
|
||||||
const aDecl = findVar(declared, 'a') !;
|
|
||||||
expect(evaluator.evaluateNode(aDecl.type !)).toEqual({
|
|
||||||
__symbolic: 'select',
|
|
||||||
expression: {__symbolic: 'reference', name: 'Foo'},
|
|
||||||
member: 'A'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return errors for unsupported expressions', () => {
|
|
||||||
const errors = program.getSourceFile('errors.ts');
|
|
||||||
const fDecl = findVar(errors, 'f') !;
|
|
||||||
expect(evaluator.evaluateNode(fDecl.initializer !))
|
|
||||||
.toEqual(
|
|
||||||
{__symbolic: 'error', message: 'Function call not supported', line: 1, character: 12});
|
|
||||||
const eDecl = findVar(errors, 'e') !;
|
|
||||||
expect(evaluator.evaluateNode(eDecl.type !)).toEqual({
|
|
||||||
__symbolic: 'error',
|
|
||||||
message: 'Could not resolve type',
|
|
||||||
line: 2,
|
|
||||||
character: 11,
|
|
||||||
context: {typeName: 'NotFound'}
|
|
||||||
});
|
|
||||||
const sDecl = findVar(errors, 's') !;
|
|
||||||
expect(evaluator.evaluateNode(sDecl.initializer !)).toEqual({
|
|
||||||
__symbolic: 'error',
|
|
||||||
message: 'Name expected',
|
|
||||||
line: 3,
|
|
||||||
character: 14,
|
|
||||||
context: {received: '1'}
|
|
||||||
});
|
|
||||||
const tDecl = findVar(errors, 't') !;
|
|
||||||
expect(evaluator.evaluateNode(tDecl.initializer !)).toEqual({
|
|
||||||
__symbolic: 'error',
|
|
||||||
message: 'Expression form not supported',
|
|
||||||
line: 4,
|
|
||||||
character: 12
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to fold an array spread', () => {
|
|
||||||
const expressions = program.getSourceFile('expressions.ts');
|
|
||||||
symbols.define('arr', [1, 2, 3, 4]);
|
|
||||||
const arrSpread = findVar(expressions, 'arrSpread') !;
|
|
||||||
expect(evaluator.evaluateNode(arrSpread.initializer !)).toEqual([0, 1, 2, 3, 4, 5]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to produce a spread expression', () => {
|
|
||||||
const expressions = program.getSourceFile('expressions.ts');
|
|
||||||
const arrSpreadRef = findVar(expressions, 'arrSpreadRef') !;
|
|
||||||
expect(evaluator.evaluateNode(arrSpreadRef.initializer !)).toEqual([
|
|
||||||
0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to handle a new expression with no arguments', () => {
|
|
||||||
const source = sourceFileOf(`
|
|
||||||
export var a = new f;
|
|
||||||
`);
|
|
||||||
const expr = findVar(source, 'a') !;
|
|
||||||
expect(evaluator.evaluateNode(expr.initializer !))
|
|
||||||
.toEqual({__symbolic: 'new', expression: {__symbolic: 'reference', name: 'f'}});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('with substitution', () => {
|
|
||||||
let evaluator: Evaluator;
|
|
||||||
const lambdaTemp = 'lambdaTemp';
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
evaluator = new Evaluator(symbols, new Map(), {
|
|
||||||
substituteExpression: (value, node) => {
|
|
||||||
if (node.kind == ts.SyntaxKind.ArrowFunction) {
|
|
||||||
return {__symbolic: 'reference', name: lambdaTemp};
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to substitute a lambda with a reference', () => {
|
|
||||||
const source = sourceFileOf(`
|
|
||||||
var b = 1;
|
|
||||||
export var a = () => b;
|
|
||||||
`);
|
|
||||||
const expr = findVar(source, 'a');
|
|
||||||
expect(evaluator.evaluateNode(expr !.initializer !))
|
|
||||||
.toEqual({__symbolic: 'reference', name: lambdaTemp});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to substitute a lambda in an expression', () => {
|
|
||||||
const source = sourceFileOf(`
|
|
||||||
var b = 1;
|
|
||||||
export var a = [
|
|
||||||
{ provide: 'someValue': useFactory: () => b }
|
|
||||||
];
|
|
||||||
`);
|
|
||||||
const expr = findVar(source, 'a');
|
|
||||||
expect(evaluator.evaluateNode(expr !.initializer !)).toEqual([
|
|
||||||
{provide: 'someValue', useFactory: {__symbolic: 'reference', name: lambdaTemp}}
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
function sourceFileOf(text: string): ts.SourceFile {
|
|
||||||
return ts.createSourceFile('test.ts', text, ts.ScriptTarget.Latest, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
const FILES: Directory = {
|
|
||||||
'directives.ts': `
|
|
||||||
export function Pipe(options: { name?: string, pure?: boolean}) {
|
|
||||||
return function(fn: Function) { }
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
'classes.ts': `
|
|
||||||
export class Value {
|
|
||||||
constructor(public name: string, public value: any) {}
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
'consts.ts': `
|
|
||||||
export var someName = 'some-name';
|
|
||||||
export var someBool = true;
|
|
||||||
export var one = 1;
|
|
||||||
export var two = 2;
|
|
||||||
export var arrImport = [1, 2, 3, 4];
|
|
||||||
`,
|
|
||||||
'expressions.ts': `
|
|
||||||
import {arrImport} from './consts';
|
|
||||||
|
|
||||||
export var someName = 'some-name';
|
|
||||||
export var someBool = true;
|
|
||||||
export var one = 1;
|
|
||||||
export var two = 2;
|
|
||||||
|
|
||||||
export var three = one + two;
|
|
||||||
export var four = two * two;
|
|
||||||
export var obj = { one: one, two: two, three: three, four: four };
|
|
||||||
export var arr = [one, two, three, four];
|
|
||||||
export var bTrue = someBool;
|
|
||||||
export var bFalse = !someBool;
|
|
||||||
export var bAnd = someBool && someBool;
|
|
||||||
export var bOr = someBool || someBool;
|
|
||||||
export var nDiv = four / two;
|
|
||||||
export var nMod = (four + one) % two;
|
|
||||||
|
|
||||||
export var bLOr = false || true; // true
|
|
||||||
export var bLAnd = true && true; // true
|
|
||||||
export var bBOr = 0x11 | 0x22; // 0x33
|
|
||||||
export var bBAnd = 0x11 & 0x03; // 0x01
|
|
||||||
export var bXor = 0x11 ^ 0x21; // 0x20
|
|
||||||
export var bEqual = 1 == <any>"1"; // true
|
|
||||||
export var bNotEqual = 1 != <any>"1"; // false
|
|
||||||
export var bIdentical = 1 === <any>"1"; // false
|
|
||||||
export var bNotIdentical = 1 !== <any>"1"; // true
|
|
||||||
export var bLessThan = 1 < 2; // true
|
|
||||||
export var bGreaterThan = 1 > 2; // false
|
|
||||||
export var bLessThanEqual = 1 <= 2; // true
|
|
||||||
export var bGreaterThanEqual = 1 >= 2; // false
|
|
||||||
export var bShiftLeft = 1 << 2; // 0x04
|
|
||||||
export var bShiftRight = -1 >> 2; // -1
|
|
||||||
export var bShiftRightU = -1 >>> 2; // 0x3fffffff
|
|
||||||
|
|
||||||
export var arrSpread = [0, ...arr, 5];
|
|
||||||
|
|
||||||
export var arrSpreadRef = [0, ...arrImport, 5];
|
|
||||||
|
|
||||||
export var recursiveA = recursiveB;
|
|
||||||
export var recursiveB = recursiveA;
|
|
||||||
`,
|
|
||||||
'A.ts': `
|
|
||||||
import {Pipe} from './directives';
|
|
||||||
|
|
||||||
@Pipe({name: 'A', pure: false})
|
|
||||||
export class A {}`,
|
|
||||||
'B.ts': `
|
|
||||||
import {Pipe} from './directives';
|
|
||||||
import {someName, someBool} from './consts';
|
|
||||||
|
|
||||||
@Pipe({name: someName, pure: someBool})
|
|
||||||
export class B {}`,
|
|
||||||
'const_expr.ts': `
|
|
||||||
function CONST_EXPR(value: any) { return value; }
|
|
||||||
export var bTrue = CONST_EXPR(true);
|
|
||||||
export var bFalse = CONST_EXPR(false);
|
|
||||||
`,
|
|
||||||
'forwardRef.ts': `
|
|
||||||
function forwardRef(value: any) { return value; }
|
|
||||||
export var bTrue = forwardRef(() => true);
|
|
||||||
export var bFalse = forwardRef(() => false);
|
|
||||||
`,
|
|
||||||
'newExpression.ts': `
|
|
||||||
import {Value} from './classes';
|
|
||||||
function CONST_EXPR(value: any) { return value; }
|
|
||||||
function forwardRef(value: any) { return value; }
|
|
||||||
export const someValue = new Value("name", 12);
|
|
||||||
export const complex = CONST_EXPR(new Value("name", forwardRef(() => 12)));
|
|
||||||
`,
|
|
||||||
'errors.ts': `
|
|
||||||
let f = () => 1;
|
|
||||||
let e: NotFound;
|
|
||||||
let s = { 1: 1, 2: 2 };
|
|
||||||
let t = typeof 12;
|
|
||||||
`,
|
|
||||||
'declared.ts': `
|
|
||||||
declare namespace Foo {
|
|
||||||
type A = string;
|
|
||||||
}
|
|
||||||
|
|
||||||
let a: Foo.A = 'some value';
|
|
||||||
`
|
|
||||||
};
|
|
@ -1,25 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import {MetadataBundler} from '../src/bundler';
|
|
||||||
import {MetadataCollector} from '../src/collector';
|
|
||||||
import {privateEntriesToIndex} from '../src/index_writer';
|
|
||||||
|
|
||||||
import {MockStringBundlerHost, SIMPLE_LIBRARY} from './bundler_spec';
|
|
||||||
|
|
||||||
describe('index_writer', () => {
|
|
||||||
it('should be able to write the index of a simple library', () => {
|
|
||||||
const host = new MockStringBundlerHost('/', SIMPLE_LIBRARY);
|
|
||||||
const bundler = new MetadataBundler('/lib/index', undefined, host);
|
|
||||||
const bundle = bundler.getMetadataBundle();
|
|
||||||
const result = privateEntriesToIndex('./index', bundle.privates);
|
|
||||||
expect(result).toContain(`export * from './index';`);
|
|
||||||
expect(result).toContain(`export {PrivateOne as ɵa} from './src/one';`);
|
|
||||||
expect(result).toContain(`export {PrivateTwo as ɵb} from './src/two/index';`);
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,422 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
import {main} from '../src/main';
|
|
||||||
|
|
||||||
import {makeTempDir} from './test_support';
|
|
||||||
|
|
||||||
describe('tsc-wrapped', () => {
|
|
||||||
let basePath: string;
|
|
||||||
let write: (fileName: string, content: string) => void;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
basePath = makeTempDir();
|
|
||||||
write = (fileName: string, content: string) => {
|
|
||||||
fs.writeFileSync(path.join(basePath, fileName), content, {encoding: 'utf-8'});
|
|
||||||
};
|
|
||||||
write('decorators.ts', '/** @Annotation */ export var Component: Function;');
|
|
||||||
fs.mkdirSync(path.join(basePath, 'dep'));
|
|
||||||
write('dep/index.ts', `
|
|
||||||
export const A = 1;
|
|
||||||
export const B = 2;
|
|
||||||
`);
|
|
||||||
write('test.ts', `
|
|
||||||
import {Component} from './decorators';
|
|
||||||
export * from './dep';
|
|
||||||
|
|
||||||
@Component({})
|
|
||||||
export class Comp {
|
|
||||||
/**
|
|
||||||
* Comment that is
|
|
||||||
* multiple lines
|
|
||||||
*/
|
|
||||||
method(x: string): void {}
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
function readOut(ext: string) {
|
|
||||||
return fs.readFileSync(path.join(basePath, 'built', `test.${ext}`), {encoding: 'utf-8'});
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should report error if project not found', () => {
|
|
||||||
main('not-exist', null as any)
|
|
||||||
.then(() => fail('should report error'))
|
|
||||||
.catch(e => expect(e.message).toContain('ENOENT'));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pre-process sources', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es2015"
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js');
|
|
||||||
// No helpers since decorators were lowered
|
|
||||||
expect(out).not.toContain('__decorate');
|
|
||||||
// Expand `export *` and fix index import
|
|
||||||
expect(out).toContain(`export { A, B } from "./dep"`);
|
|
||||||
// Annotated for Closure compiler
|
|
||||||
expect(out).toContain('* @param {?} x');
|
|
||||||
// Comments should stay multi-line
|
|
||||||
expect(out).not.toContain('Comment that is multiple lines');
|
|
||||||
// Decorator is now an annotation
|
|
||||||
expect(out).toMatch(/Comp.decorators = \[\s+\{ type: Component/);
|
|
||||||
const decl = readOut('d.ts');
|
|
||||||
expect(decl).toContain('declare class Comp');
|
|
||||||
const metadata = readOut('metadata.json');
|
|
||||||
expect(metadata).toContain('"Comp":{"__symbolic":"class"');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should pre-process sources using config from vinyl like object', (done) => {
|
|
||||||
const config = {
|
|
||||||
path: basePath + '/tsconfig.json',
|
|
||||||
contents: new Buffer(JSON.stringify({
|
|
||||||
compilerOptions: {
|
|
||||||
experimentalDecorators: true,
|
|
||||||
types: [],
|
|
||||||
outDir: 'built',
|
|
||||||
declaration: true,
|
|
||||||
moduleResolution: 'node',
|
|
||||||
target: 'es2015'
|
|
||||||
},
|
|
||||||
angularCompilerOptions: {annotateForClosureCompiler: true},
|
|
||||||
files: ['test.ts']
|
|
||||||
}))
|
|
||||||
};
|
|
||||||
|
|
||||||
main(config, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js');
|
|
||||||
// Expand `export *` and fix index import
|
|
||||||
expect(out).toContain(`export { A, B } from "./dep"`);
|
|
||||||
// Annotated for Closure compiler
|
|
||||||
expect(out).toContain('* @param {?} x');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow all options disabled', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": false,
|
|
||||||
"module": "es2015",
|
|
||||||
"moduleResolution": "node"
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": false,
|
|
||||||
"annotationsAs": "decorators",
|
|
||||||
"skipMetadataEmit": true,
|
|
||||||
"skipTemplateCodegen": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js');
|
|
||||||
// TypeScript's decorator emit
|
|
||||||
expect(out).toContain('__decorate');
|
|
||||||
// Not annotated for Closure compiler
|
|
||||||
expect(out).not.toContain('* @param {?} x');
|
|
||||||
expect(() => fs.accessSync(path.join(basePath, 'built', 'test.metadata.json'))).toThrow();
|
|
||||||
expect(() => fs.accessSync(path.join(basePath, 'built', 'test.d.ts'))).toThrow();
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow all options disabled with metadata emit', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": false,
|
|
||||||
"module": "es2015",
|
|
||||||
"moduleResolution": "node"
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": false,
|
|
||||||
"annotationsAs": "decorators",
|
|
||||||
"skipMetadataEmit": false,
|
|
||||||
"skipTemplateCodegen": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js');
|
|
||||||
// TypeScript's decorator emit
|
|
||||||
expect(out).toContain('__decorate');
|
|
||||||
// Not annotated for Closure compiler
|
|
||||||
expect(out).not.toContain('* @param {?} x');
|
|
||||||
expect(() => fs.accessSync(path.join(basePath, 'built', 'test.d.ts'))).toThrow();
|
|
||||||
const metadata = readOut('metadata.json');
|
|
||||||
expect(metadata).toContain('"Comp":{"__symbolic":"class"');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should allow JSDoc annotations without decorator downleveling', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true,
|
|
||||||
"annotationsAs": "decorators"
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
main(basePath, {basePath}).then(() => done()).catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
xit('should run quickly (performance baseline)', (done) => {
|
|
||||||
for (let i = 0; i < 1000; i++) {
|
|
||||||
write(`input${i}.ts`, `
|
|
||||||
import {Component} from './decorators';
|
|
||||||
@Component({})
|
|
||||||
export class Input${i} {
|
|
||||||
private __brand: string;
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"diagnostics": true
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": false,
|
|
||||||
"annotationsAs": "decorators",
|
|
||||||
"skipMetadataEmit": true
|
|
||||||
},
|
|
||||||
"include": ["input*.ts"]
|
|
||||||
}`);
|
|
||||||
console.time('BASELINE');
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
console.timeEnd('BASELINE');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
xit('should run quickly (performance test)', (done) => {
|
|
||||||
for (let i = 0; i < 1000; i++) {
|
|
||||||
write(`input${i}.ts`, `
|
|
||||||
import {Component} from './decorators';
|
|
||||||
@Component({})
|
|
||||||
export class Input${i} {
|
|
||||||
private __brand: string;
|
|
||||||
}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"diagnostics": true,
|
|
||||||
"skipLibCheck": true
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"include": ["input*.ts"]
|
|
||||||
}`);
|
|
||||||
console.time('TSICKLE');
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
console.timeEnd('TSICKLE');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should produce valid source maps', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es2015",
|
|
||||||
"sourceMap": true
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js.map');
|
|
||||||
expect(out).toContain('"sources":["../test.ts"]');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should accept input source maps', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es2015",
|
|
||||||
"sourceMap": true
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
// Provide a file called test.ts that has an inline source map
|
|
||||||
// which says that it was transpiled from a file other_test.ts
|
|
||||||
// with exactly the same content.
|
|
||||||
const inputSourceMap =
|
|
||||||
`{"version":3,"sources":["other_test.ts"],"names":[],"mappings":"AAAA,MAAM,EAAE,EAAE,CAAC","file":"../test.ts","sourceRoot":""}`;
|
|
||||||
const encodedSourceMap = new Buffer(inputSourceMap, 'utf8').toString('base64');
|
|
||||||
write('test.ts', `const x = 3;
|
|
||||||
//# sourceMappingURL=data:application/json;base64,${encodedSourceMap}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js.map');
|
|
||||||
expect(out).toContain('"sources":["other_test.ts"]');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it(`should accept input source maps that don't match the file name`, (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es2015",
|
|
||||||
"sourceMap": true
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
// Provide a file called test.ts that has an inline source map
|
|
||||||
// which says that it was transpiled from a file other_test.ts
|
|
||||||
// with exactly the same content.
|
|
||||||
const inputSourceMap =
|
|
||||||
`{"version":3,"sources":["other_test.ts"],"names":[],"mappings":"AAAA,MAAM,EAAE,EAAE,CAAC","file":"test.ts","sourceRoot":""}`;
|
|
||||||
const encodedSourceMap = new Buffer(inputSourceMap, 'utf8').toString('base64');
|
|
||||||
write('test.ts', `const x = 3;
|
|
||||||
//# sourceMappingURL=data:application/json;base64,${encodedSourceMap}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const out = readOut('js.map');
|
|
||||||
expect(out).toContain('"sources":["other_test.ts"]');
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should expand shorthand imports for ES2015 modules', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es2015",
|
|
||||||
"module": "es2015"
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const fileOutput = readOut('js');
|
|
||||||
expect(fileOutput).toContain(`export { A, B } from "./dep"`);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not expand shorthand imports for ES5 CommonJS modules', (done) => {
|
|
||||||
write('tsconfig.json', `{
|
|
||||||
"compilerOptions": {
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"types": [],
|
|
||||||
"outDir": "built",
|
|
||||||
"declaration": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"target": "es5",
|
|
||||||
"module": "commonjs"
|
|
||||||
},
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"annotateForClosureCompiler": true
|
|
||||||
},
|
|
||||||
"files": ["test.ts"]
|
|
||||||
}`);
|
|
||||||
|
|
||||||
main(basePath, {basePath})
|
|
||||||
.then(() => {
|
|
||||||
const fileOutput = readOut('js');
|
|
||||||
expect(fileOutput).toContain(`var dep_1 = require("./dep");`);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(e => done.fail(e));
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
@ -1,133 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
import {isMetadataGlobalReferenceExpression} from '../src/schema';
|
|
||||||
import {Symbols} from '../src/symbols';
|
|
||||||
|
|
||||||
import {Directory, Host, expectNoDiagnostics} from './typescript.mocks';
|
|
||||||
|
|
||||||
describe('Symbols', () => {
|
|
||||||
let symbols: Symbols;
|
|
||||||
const someValue = 'some-value';
|
|
||||||
|
|
||||||
beforeEach(() => symbols = new Symbols(null as any as ts.SourceFile));
|
|
||||||
|
|
||||||
it('should be able to add a symbol', () => symbols.define('someSymbol', someValue));
|
|
||||||
|
|
||||||
beforeEach(() => symbols.define('someSymbol', someValue));
|
|
||||||
|
|
||||||
it('should be able to `has` a symbol', () => expect(symbols.has('someSymbol')).toBeTruthy());
|
|
||||||
it('should be able to `get` a symbol value',
|
|
||||||
() => expect(symbols.resolve('someSymbol')).toBe(someValue));
|
|
||||||
it('should be able to `get` a symbol value',
|
|
||||||
() => expect(symbols.resolve('someSymbol')).toBe(someValue));
|
|
||||||
it('should be able to determine symbol is missing',
|
|
||||||
() => expect(symbols.has('missingSymbol')).toBeFalsy());
|
|
||||||
it('should return undefined from `get` for a missing symbol',
|
|
||||||
() => expect(symbols.resolve('missingSymbol')).toBeUndefined());
|
|
||||||
|
|
||||||
let host: ts.LanguageServiceHost;
|
|
||||||
let service: ts.LanguageService;
|
|
||||||
let program: ts.Program;
|
|
||||||
let expressions: ts.SourceFile;
|
|
||||||
let imports: ts.SourceFile;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
host = new Host(FILES, ['consts.ts', 'expressions.ts', 'imports.ts']);
|
|
||||||
service = ts.createLanguageService(host);
|
|
||||||
program = service.getProgram();
|
|
||||||
expressions = program.getSourceFile('expressions.ts');
|
|
||||||
imports = program.getSourceFile('imports.ts');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should not have syntax errors in the test sources', () => {
|
|
||||||
expectNoDiagnostics(service.getCompilerOptionsDiagnostics());
|
|
||||||
for (const sourceFile of program.getSourceFiles()) {
|
|
||||||
expectNoDiagnostics(service.getSyntacticDiagnostics(sourceFile.fileName));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to find the source files', () => {
|
|
||||||
expect(expressions).toBeDefined();
|
|
||||||
expect(imports).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to create symbols for a source file', () => {
|
|
||||||
const symbols = new Symbols(expressions);
|
|
||||||
expect(symbols).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
it('should be able to find symbols in expression', () => {
|
|
||||||
const symbols = new Symbols(expressions);
|
|
||||||
expect(symbols.has('someName')).toBeTruthy();
|
|
||||||
expect(symbols.resolve('someName'))
|
|
||||||
.toEqual({__symbolic: 'reference', module: './consts', name: 'someName'});
|
|
||||||
expect(symbols.has('someBool')).toBeTruthy();
|
|
||||||
expect(symbols.resolve('someBool'))
|
|
||||||
.toEqual({__symbolic: 'reference', module: './consts', name: 'someBool'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to detect a * import', () => {
|
|
||||||
const symbols = new Symbols(imports);
|
|
||||||
expect(symbols.resolve('b')).toEqual({__symbolic: 'reference', module: 'b'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to detect importing a default export', () => {
|
|
||||||
const symbols = new Symbols(imports);
|
|
||||||
expect(symbols.resolve('d')).toEqual({__symbolic: 'reference', module: 'd', default: true});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to import a renamed symbol', () => {
|
|
||||||
const symbols = new Symbols(imports);
|
|
||||||
expect(symbols.resolve('g')).toEqual({__symbolic: 'reference', name: 'f', module: 'f'});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should be able to resolve any symbol in core global scope', () => {
|
|
||||||
const core = program.getSourceFiles().find(source => source.fileName.endsWith('lib.d.ts'));
|
|
||||||
expect(core).toBeDefined();
|
|
||||||
const visit = (node: ts.Node): boolean => {
|
|
||||||
switch (node.kind) {
|
|
||||||
case ts.SyntaxKind.VariableStatement:
|
|
||||||
case ts.SyntaxKind.VariableDeclarationList:
|
|
||||||
return !!ts.forEachChild(node, visit);
|
|
||||||
case ts.SyntaxKind.VariableDeclaration:
|
|
||||||
const variableDeclaration = <ts.VariableDeclaration>node;
|
|
||||||
const nameNode = <ts.Identifier>variableDeclaration.name;
|
|
||||||
const name = nameNode.text;
|
|
||||||
const result = symbols.resolve(name);
|
|
||||||
expect(isMetadataGlobalReferenceExpression(result) && result.name).toEqual(name);
|
|
||||||
|
|
||||||
// Ignore everything after Float64Array as it is IE specific.
|
|
||||||
return name === 'Float64Array';
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
ts.forEachChild(core !, visit);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const FILES: Directory = {
|
|
||||||
'consts.ts': `
|
|
||||||
export var someName = 'some-name';
|
|
||||||
export var someBool = true;
|
|
||||||
export var one = 1;
|
|
||||||
export var two = 2;
|
|
||||||
`,
|
|
||||||
'expressions.ts': `
|
|
||||||
import {someName, someBool, one, two} from './consts';
|
|
||||||
`,
|
|
||||||
'imports.ts': `
|
|
||||||
import * as b from 'b';
|
|
||||||
import 'c';
|
|
||||||
import d from 'd';
|
|
||||||
import {f as g} from 'f';
|
|
||||||
`
|
|
||||||
};
|
|
@ -1,28 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as os from 'os';
|
|
||||||
import * as path from 'path';
|
|
||||||
|
|
||||||
const tmpdir = process.env.TEST_TMPDIR || os.tmpdir();
|
|
||||||
|
|
||||||
export function writeTempFile(name: string, contents: string): string {
|
|
||||||
// TEST_TMPDIR is set by bazel.
|
|
||||||
const id = (Math.random() * 1000000).toFixed(0);
|
|
||||||
const fn = path.join(tmpdir, `tmp.${id}.${name}`);
|
|
||||||
fs.writeFileSync(fn, contents);
|
|
||||||
return fn;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function makeTempDir(): string {
|
|
||||||
const id = (Math.random() * 1000000).toFixed(0);
|
|
||||||
const dir = path.join(tmpdir, `tmp.${id}`);
|
|
||||||
fs.mkdirSync(dir);
|
|
||||||
return dir;
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
import {Tsc} from '../src/tsc';
|
|
||||||
import {VinylFile} from '../src/vinyl_file';
|
|
||||||
|
|
||||||
describe('options parsing', () => {
|
|
||||||
|
|
||||||
const configData = `
|
|
||||||
{
|
|
||||||
"angularCompilerOptions": {
|
|
||||||
"googleClosureOutput": true
|
|
||||||
},
|
|
||||||
"compilerOptions": {
|
|
||||||
"module": "commonjs",
|
|
||||||
"outDir": "built"
|
|
||||||
}
|
|
||||||
}`;
|
|
||||||
|
|
||||||
const tsc = new Tsc(() => configData, () => ['tsconfig.json']);
|
|
||||||
const config = {path: 'basePath/tsconfig.json', contents: new Buffer(configData)};
|
|
||||||
|
|
||||||
it('should combine all options into ngOptions', () => {
|
|
||||||
const {parsed, ngOptions} =
|
|
||||||
tsc.readConfiguration('projectDir', 'basePath', {target: ts.ScriptTarget.ES2015});
|
|
||||||
|
|
||||||
expect(ngOptions).toEqual({
|
|
||||||
genDir: 'basePath',
|
|
||||||
googleClosureOutput: true,
|
|
||||||
module: ts.ModuleKind.CommonJS,
|
|
||||||
outDir: 'basePath/built',
|
|
||||||
configFilePath: undefined,
|
|
||||||
target: ts.ScriptTarget.ES2015
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should combine all options into ngOptions from vinyl like object', () => {
|
|
||||||
const {parsed, ngOptions} = tsc.readConfiguration(config as VinylFile, 'basePath');
|
|
||||||
|
|
||||||
expect(ngOptions).toEqual({
|
|
||||||
genDir: 'basePath',
|
|
||||||
googleClosureOutput: true,
|
|
||||||
module: ts.ModuleKind.CommonJS,
|
|
||||||
outDir: 'basePath/built',
|
|
||||||
configFilePath: undefined
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
@ -1,204 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright Google Inc. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
|
||||||
* found in the LICENSE file at https://angular.io/license
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as fs from 'fs';
|
|
||||||
import * as ts from 'typescript';
|
|
||||||
|
|
||||||
export interface Directory { [name: string]: (Directory|string); }
|
|
||||||
|
|
||||||
export class Host implements ts.LanguageServiceHost {
|
|
||||||
private overrides = new Map<string, string>();
|
|
||||||
private version = 1;
|
|
||||||
|
|
||||||
constructor(private directory: Directory, private scripts: string[]) {}
|
|
||||||
|
|
||||||
getCompilationSettings(): ts.CompilerOptions {
|
|
||||||
return {
|
|
||||||
experimentalDecorators: true,
|
|
||||||
module: ts.ModuleKind.CommonJS,
|
|
||||||
target: ts.ScriptTarget.ES5
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
getScriptFileNames(): string[] { return this.scripts; }
|
|
||||||
|
|
||||||
getScriptVersion(fileName: string): string { return this.version.toString(); }
|
|
||||||
|
|
||||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot|undefined {
|
|
||||||
const content = this.getFileContent(fileName);
|
|
||||||
if (content) return ts.ScriptSnapshot.fromString(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
fileExists(fileName: string): boolean { return this.getFileContent(fileName) != null; }
|
|
||||||
|
|
||||||
getCurrentDirectory(): string { return '/'; }
|
|
||||||
|
|
||||||
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; }
|
|
||||||
|
|
||||||
overrideFile(fileName: string, content: string) {
|
|
||||||
this.overrides.set(fileName, content);
|
|
||||||
this.version++;
|
|
||||||
}
|
|
||||||
|
|
||||||
addFile(fileName: string) {
|
|
||||||
this.scripts.push(fileName);
|
|
||||||
this.version++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getFileContent(fileName: string): string|undefined {
|
|
||||||
if (this.overrides.has(fileName)) {
|
|
||||||
return this.overrides.get(fileName);
|
|
||||||
}
|
|
||||||
if (fileName.endsWith('lib.d.ts')) {
|
|
||||||
return fs.readFileSync(ts.getDefaultLibFilePath(this.getCompilationSettings()), 'utf8');
|
|
||||||
}
|
|
||||||
const current = open(this.directory, fileName);
|
|
||||||
if (typeof current === 'string') return current;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function open(directory: Directory, fileName: string): Directory|string|undefined {
|
|
||||||
// Path might be normalized by the current node environment. But it could also happen that this
|
|
||||||
// path directly comes from the compiler in POSIX format. Support both separators for development.
|
|
||||||
const names = fileName.split(/[\\/]/);
|
|
||||||
let current: Directory|string = directory;
|
|
||||||
if (names.length && names[0] === '') names.shift();
|
|
||||||
for (const name of names) {
|
|
||||||
if (!current || typeof current === 'string') return undefined;
|
|
||||||
current = current[name];
|
|
||||||
}
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockNode implements ts.Node {
|
|
||||||
constructor(
|
|
||||||
public kind: ts.SyntaxKind = ts.SyntaxKind.Identifier, public flags: ts.NodeFlags = 0,
|
|
||||||
public pos: number = 0, public end: number = 0) {}
|
|
||||||
getSourceFile(): ts.SourceFile { return null as any as ts.SourceFile; }
|
|
||||||
getChildCount(sourceFile?: ts.SourceFile): number { return 0; }
|
|
||||||
getChildAt(index: number, sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; }
|
|
||||||
getChildren(sourceFile?: ts.SourceFile): ts.Node[] { return []; }
|
|
||||||
getStart(sourceFile?: ts.SourceFile): number { return 0; }
|
|
||||||
getFullStart(): number { return 0; }
|
|
||||||
getEnd(): number { return 0; }
|
|
||||||
getWidth(sourceFile?: ts.SourceFile): number { return 0; }
|
|
||||||
getFullWidth(): number { return 0; }
|
|
||||||
getLeadingTriviaWidth(sourceFile?: ts.SourceFile): number { return 0; }
|
|
||||||
getFullText(sourceFile?: ts.SourceFile): string { return ''; }
|
|
||||||
getText(sourceFile?: ts.SourceFile): string { return ''; }
|
|
||||||
getFirstToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; }
|
|
||||||
getLastToken(sourceFile?: ts.SourceFile): ts.Node { return null as any as ts.Node; }
|
|
||||||
forEachChild<T>(
|
|
||||||
cbNode: (node: ts.Node) => T | undefined,
|
|
||||||
cbNodeArray?: (nodes: ts.NodeArray<ts.Node>) => T | undefined): T|undefined {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockIdentifier extends MockNode implements ts.Identifier {
|
|
||||||
public text: string;
|
|
||||||
// tslint:disable
|
|
||||||
public _primaryExpressionBrand: any;
|
|
||||||
public _memberExpressionBrand: any;
|
|
||||||
public _leftHandSideExpressionBrand: any;
|
|
||||||
public _incrementExpressionBrand: any;
|
|
||||||
public _unaryExpressionBrand: any;
|
|
||||||
public _expressionBrand: any;
|
|
||||||
public _updateExpressionBrand: any;
|
|
||||||
// tslint:enable
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public name: string, public kind: ts.SyntaxKind.Identifier = ts.SyntaxKind.Identifier,
|
|
||||||
flags: ts.NodeFlags = 0, pos: number = 0, end: number = 0) {
|
|
||||||
super(kind, flags, pos, end);
|
|
||||||
this.text = name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockVariableDeclaration extends MockNode implements ts.VariableDeclaration {
|
|
||||||
// tslint:disable-next-line
|
|
||||||
public _declarationBrand: any;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public name: ts.Identifier,
|
|
||||||
public kind: ts.SyntaxKind.VariableDeclaration = ts.SyntaxKind.VariableDeclaration,
|
|
||||||
flags: ts.NodeFlags = 0, pos: number = 0, end: number = 0) {
|
|
||||||
super(kind, flags, pos, end);
|
|
||||||
}
|
|
||||||
|
|
||||||
static of (name: string): MockVariableDeclaration {
|
|
||||||
return new MockVariableDeclaration(new MockIdentifier(name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MockSymbol implements ts.Symbol {
|
|
||||||
constructor(
|
|
||||||
public name: string, private node: ts.Declaration = MockVariableDeclaration.of(name),
|
|
||||||
public flags: ts.SymbolFlags = 0) {}
|
|
||||||
|
|
||||||
getFlags(): ts.SymbolFlags { return this.flags; }
|
|
||||||
getName(): string { return this.name; }
|
|
||||||
getDeclarations(): ts.Declaration[] { return [this.node]; }
|
|
||||||
getDocumentationComment(): ts.SymbolDisplayPart[] { return []; }
|
|
||||||
// TODO(vicb): removed in TS 2.2
|
|
||||||
getJsDocTags(): any[] { return []; }
|
|
||||||
|
|
||||||
static of (name: string): MockSymbol { return new MockSymbol(name); }
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expectNoDiagnostics(diagnostics: ts.Diagnostic[]) {
|
|
||||||
for (const diagnostic of diagnostics) {
|
|
||||||
const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
|
|
||||||
if (diagnostic.file && diagnostic.start) {
|
|
||||||
const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
|
||||||
// tslint:disable-next-line:no-console
|
|
||||||
console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
expect(diagnostics.length).toBe(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function expectValidSources(service: ts.LanguageService, program: ts.Program) {
|
|
||||||
expectNoDiagnostics(service.getCompilerOptionsDiagnostics());
|
|
||||||
for (const sourceFile of program.getSourceFiles()) {
|
|
||||||
expectNoDiagnostics(service.getSyntacticDiagnostics(sourceFile.fileName));
|
|
||||||
expectNoDiagnostics(service.getSemanticDiagnostics(sourceFile.fileName));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function allChildren<T>(node: ts.Node, cb: (node: ts.Node) => T | undefined): T|undefined {
|
|
||||||
return ts.forEachChild(node, child => cb(node) || allChildren(child, cb));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findClass(sourceFile: ts.SourceFile, name: string): ts.ClassDeclaration|undefined {
|
|
||||||
return ts.forEachChild(
|
|
||||||
sourceFile, node => isClass(node) && isNamed(node.name, name) ? node : undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findVar(sourceFile: ts.SourceFile, name: string): ts.VariableDeclaration|undefined {
|
|
||||||
return allChildren(
|
|
||||||
sourceFile, node => isVar(node) && isNamed(node.name, name) ? node : undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function findVarInitializer(sourceFile: ts.SourceFile, name: string): ts.Expression {
|
|
||||||
const v = findVar(sourceFile, name);
|
|
||||||
expect(v && v.initializer).toBeDefined();
|
|
||||||
return v !.initializer !;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isClass(node: ts.Node): node is ts.ClassDeclaration {
|
|
||||||
return node.kind === ts.SyntaxKind.ClassDeclaration;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNamed(node: ts.Node | undefined, name: string): node is ts.Identifier {
|
|
||||||
return !!node && node.kind === ts.SyntaxKind.Identifier && (<ts.Identifier>node).text === name;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isVar(node: ts.Node): node is ts.VariableDeclaration {
|
|
||||||
return node.kind === ts.SyntaxKind.VariableDeclaration;
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"outDir": "../../dist/packages-dist/tsc-wrapped",
|
|
||||||
"types": [
|
|
||||||
"node",
|
|
||||||
"jasmine"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": ".",
|
|
||||||
"declaration": true,
|
|
||||||
"emitDecoratorMetadata": true,
|
|
||||||
"experimentalDecorators": true,
|
|
||||||
"module": "commonjs",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"outDir": "../../dist/all/@angular/tsc-wrapped",
|
|
||||||
"strictNullChecks": true,
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"noFallthroughCasesInSwitch": true,
|
|
||||||
"paths": {
|
|
||||||
},
|
|
||||||
"rootDir": ".",
|
|
||||||
"sourceMap": true,
|
|
||||||
"inlineSources": true,
|
|
||||||
"lib": ["es6", "dom"],
|
|
||||||
"target": "es5",
|
|
||||||
"skipLibCheck": true
|
|
||||||
}
|
|
||||||
}
|
|
24
packages/tsconfig-metadata.json
Normal file
24
packages/tsconfig-metadata.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
|
||||||
|
"compilerOptions": {
|
||||||
|
"paths": {
|
||||||
|
"@angular/*": ["./*"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"exclude": [
|
||||||
|
"bazel",
|
||||||
|
"compiler/test",
|
||||||
|
"compiler-cli/integrationtest",
|
||||||
|
"platform-server/integrationtest",
|
||||||
|
"common/locales",
|
||||||
|
"examples",
|
||||||
|
"**/*_spec.ts",
|
||||||
|
"**/*.spec.ts"
|
||||||
|
],
|
||||||
|
|
||||||
|
"angularCompilerOptions": {
|
||||||
|
"skipTemplateCodegen": true
|
||||||
|
}
|
||||||
|
}
|
@ -28,7 +28,7 @@
|
|||||||
"bazel",
|
"bazel",
|
||||||
"compiler-cli/integrationtest",
|
"compiler-cli/integrationtest",
|
||||||
"platform-server/integrationtest",
|
"platform-server/integrationtest",
|
||||||
"tsc-wrapped",
|
|
||||||
"common/locales"
|
"common/locales"
|
||||||
]
|
]
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@
|
|||||||
],
|
],
|
||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "static.js",
|
"flatModuleOutFile": "static.js",
|
||||||
"flatModuleId": "@angular/upgrade/static"
|
"flatModuleId": "@angular/upgrade/static"
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"annotateForClosureCompiler": true,
|
"annotateForClosureCompiler": true,
|
||||||
"strictMetadataEmit": true,
|
"strictMetadataEmit": false,
|
||||||
|
"skipTemplateCodegen": true,
|
||||||
"flatModuleOutFile": "upgrade.js",
|
"flatModuleOutFile": "upgrade.js",
|
||||||
"flatModuleId": "@angular/upgrade"
|
"flatModuleId": "@angular/upgrade"
|
||||||
}
|
}
|
||||||
|
@ -52,14 +52,13 @@ fi
|
|||||||
|
|
||||||
travisFoldStart "tsc tools"
|
travisFoldStart "tsc tools"
|
||||||
$(npm bin)/tsc -p tools
|
$(npm bin)/tsc -p tools
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig-build.json
|
$(npm bin)/tsc -p packages/compiler/tsconfig-tools.json
|
||||||
cp packages/tsc-wrapped/package.json dist/packages-dist/tsc-wrapped
|
$(npm bin)/tsc -p packages/compiler-cli/tsconfig-tools.json
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped/tsconfig.json
|
|
||||||
cp packages/tsc-wrapped/package.json dist/all/@angular/tsc-wrapped
|
|
||||||
travisFoldEnd "tsc tools"
|
travisFoldEnd "tsc tools"
|
||||||
|
|
||||||
|
|
||||||
travisFoldStart "tsc all"
|
travisFoldStart "tsc all"
|
||||||
node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main -p packages
|
$(npm bin)/tsc -p packages
|
||||||
node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main -p modules
|
node dist/tools/@angular/compiler-cli/src/main -p packages/tsconfig-metadata.json
|
||||||
|
$(npm bin)/tsc -p modules
|
||||||
travisFoldEnd "tsc all"
|
travisFoldEnd "tsc all"
|
||||||
|
@ -5,7 +5,7 @@ set -u -e -o pipefail
|
|||||||
|
|
||||||
# These ones can be `npm link`ed for fast development
|
# These ones can be `npm link`ed for fast development
|
||||||
LINKABLE_PKGS=(
|
LINKABLE_PKGS=(
|
||||||
$(pwd)/dist/packages-dist/{common,forms,core,compiler,compiler-cli,platform-{browser,server},platform-browser-dynamic,router,http,animations,tsc-wrapped}
|
$(pwd)/dist/packages-dist/{common,forms,core,compiler,compiler-cli,platform-{browser,server},platform-browser-dynamic,router,http,animations}
|
||||||
)
|
)
|
||||||
|
|
||||||
TYPESCRIPT_2_4=typescript@2.4.x
|
TYPESCRIPT_2_4=typescript@2.4.x
|
||||||
@ -46,10 +46,10 @@ cp -v package.json $TMP
|
|||||||
#./node_modules/.bin/ngc -p tsconfig-build.json --i18nFile=src/messages.fi.xtb --locale=fi --i18nFormat=xtb
|
#./node_modules/.bin/ngc -p tsconfig-build.json --i18nFile=src/messages.fi.xtb --locale=fi --i18nFormat=xtb
|
||||||
|
|
||||||
# Generate the metadata for the third-party modules
|
# Generate the metadata for the third-party modules
|
||||||
node ./node_modules/@angular/tsc-wrapped/src/main -p third_party_src/tsconfig-build.json
|
./node_modules/.bin/ngc -p third_party_src/tsconfig-build.json
|
||||||
|
|
||||||
# Generate the the bundle modules
|
# Generate the the bundle modules
|
||||||
node ./node_modules/@angular/tsc-wrapped/src/main -p flat_module/tsconfig-build.json
|
./node_modules/.bin/ngc -p flat_module/tsconfig-build.json
|
||||||
|
|
||||||
# Copy the html files from source to the emitted output
|
# Copy the html files from source to the emitted output
|
||||||
cp flat_module/src/*.html node_modules/flat_module/src
|
cp flat_module/src/*.html node_modules/flat_module/src
|
||||||
|
@ -9,8 +9,6 @@ source ${thisDir}/_travis-fold.sh
|
|||||||
|
|
||||||
# Run unit tests for our tools/ directory
|
# Run unit tests for our tools/ directory
|
||||||
travisFoldStart "test.unit.tools"
|
travisFoldStart "test.unit.tools"
|
||||||
node ./dist/tools/tsc-watch/ tools runCmdsOnly
|
|
||||||
|
|
||||||
# TODO(i) could this be rolled into the tools tests above? why is it separate?
|
# TODO(i) could this be rolled into the tools tests above? why is it separate?
|
||||||
travisFoldStart "test.unit.validate-commit-message"
|
travisFoldStart "test.unit.validate-commit-message"
|
||||||
(
|
(
|
||||||
@ -30,8 +28,7 @@ travisFoldEnd "test.unit.node"
|
|||||||
# rebuild to revert files in @angular/compiler/test
|
# rebuild to revert files in @angular/compiler/test
|
||||||
# TODO(tbosch): remove this and teach karma to serve the right files
|
# TODO(tbosch): remove this and teach karma to serve the right files
|
||||||
travisFoldStart "test.unit.rebuildHack"
|
travisFoldStart "test.unit.rebuildHack"
|
||||||
node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main -p packages/tsconfig.json
|
node dist/tools/@angular/compiler-cli/src/main -p packages/tsconfig-metadata.json
|
||||||
node --max-old-space-size=3000 dist/packages-dist/tsc-wrapped/src/main -p modules/tsconfig.json
|
|
||||||
travisFoldStart "test.unit.rebuildHack"
|
travisFoldStart "test.unit.rebuildHack"
|
||||||
|
|
||||||
|
|
||||||
|
9
test.sh
9
test.sh
@ -19,12 +19,13 @@ else
|
|||||||
fi
|
fi
|
||||||
echo "Compiling tools..."
|
echo "Compiling tools..."
|
||||||
$(npm bin)/tsc -p tools
|
$(npm bin)/tsc -p tools
|
||||||
$(npm bin)/tsc -p packages/tsc-wrapped
|
|
||||||
if [[ $1 == 'node' ]]; then
|
if [[ $1 == 'node' ]]; then
|
||||||
# Note: .metadata.json files are needed for the language service tests!
|
# Note: .metadata.json files are needed for the language service tests!
|
||||||
echo "Creating .metadata.json files..."
|
echo "Building compiler..."
|
||||||
node --max-old-space-size=3000 dist/all/@angular/tsc-wrapped/src/main -p packages
|
$(npm bin)/tsc -p packages/compiler/tsconfig-tools.json
|
||||||
node --max-old-space-size=3000 dist/all/@angular/tsc-wrapped/src/main -p modules
|
$(npm bin)/tsc -p packages/compiler-cli/tsconfig-tools.json
|
||||||
|
echo "Creating packages .metadata.json files..."
|
||||||
|
node --max-old-space-size=3000 dist/tools/@angular/compiler-cli/src/main -p packages/tsconfig-metadata.json
|
||||||
fi
|
fi
|
||||||
node dist/tools/tsc-watch/ $1 watch $2
|
node dist/tools/tsc-watch/ $1 watch $2
|
||||||
fi
|
fi
|
||||||
|
@ -16,9 +16,6 @@ module.exports = (gulp) => () => {
|
|||||||
// Ignore node_modules directories
|
// Ignore node_modules directories
|
||||||
'!**/node_modules/**',
|
'!**/node_modules/**',
|
||||||
|
|
||||||
// Ignore TypeScript mocks because it's not managed by us
|
|
||||||
'!./tools/@angular/tsc-wrapped/test/typescript.mocks.ts',
|
|
||||||
|
|
||||||
// Ignore generated files due to lack of copyright header
|
// Ignore generated files due to lack of copyright header
|
||||||
// todo(alfaproject): make generated files lintable
|
// todo(alfaproject): make generated files lintable
|
||||||
'!**/*.d.ts',
|
'!**/*.d.ts',
|
||||||
|
@ -2,7 +2,7 @@ const fs = require('fs');
|
|||||||
const path = require('path');
|
const path = require('path');
|
||||||
const sourceMapTest = require('../source-map-test');
|
const sourceMapTest = require('../source-map-test');
|
||||||
|
|
||||||
const excludedPackages = ['bazel', 'tsc-wrapped', 'benchpress', 'compiler-cli', 'language-service'];
|
const excludedPackages = ['bazel', 'benchpress', 'compiler-cli', 'language-service'];
|
||||||
|
|
||||||
module.exports = (gulp) => () => {
|
module.exports = (gulp) => () => {
|
||||||
const packageDir = path.resolve(process.cwd(), 'dist/packages-dist/');
|
const packageDir = path.resolve(process.cwd(), 'dist/packages-dist/');
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user