我正在尝试使用以下配置为我的打字稿应用程序引用自定义模块快捷方式(即使用ts
路径映射功能)。
项目结构
dist/
src/
lyrics/
... ts files
app/
... ts files
完整的项目结构在这里:github.com/adadgio/npm-lyrics-ts,当然没有提交dist文件夹)
tsconfig.json
{
"compilerOptions": {
"outDir": "dist",
"module": "commonjs",
"target": "es6",
"sourceMap": true,
"moduleResolution": "node",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"removeComments": true,
"noImplicitAny": false,
"baseUrl": ".",
"paths": {
"*": ["src/lyrics/*"], // to much here !! but none work
"zutils/*": ["./src/lyrics/*", "src/lyrics/utils/*", "dist/lyrics/utils/*"]
},
"rootDir": "."
},
"exclude": [
"dist",
"node_modules"
],
"include": [
"src/**/*.ts"
]
}
当我运行我的npm start / compile或watch脚本时,我没有得到Typescript错误。以下工作(Atom是我的IDE)
// string-utils.ts does exist, no IDE error, typescript DOES compile
`import { StringUtils } from 'zutils/string-utils';`
但是我接着发现NodeJS错误:
Error: Cannot find module 'zutils/string-utils'
at Function.Module._resolveFilename (module.js:470:15)
at Function.Module._load (module.js:418:25)
at Module.require (module.js:498:17)
at require (internal/module.js:20:19)
at Object.<anonymous> (/home/adadgio/WebServer/projects/adadgio/npm-lyrics-ts/src/index.ts:7:1)
at Module._compile (module.js:571:32)
at Module.m._compile (/home/adadgio/WebServer/projects/adadgio/npm-lyrics-ts/node_modules/ts-node/src/index.ts:413:23)
at Module._extensions..js (module.js:580:10)
at Object.require.extensions.(anonymous function) [as .ts] (/home/adadgio/WebServer/projects/adadgio/npm-lyrics-ts/node_modules/ts-node/src/index.ts:416:12)
at Module.load (module.js:488:32)
看起来该模块正试图从node_modules
文件夹中解析。我已阅读有关Typescript路径映射的文档,但我无法使其工作。
答案 0 :(得分:2)
我对此进行了大量研究。 我正在使用原子,打字稿和nodejs。
问题是,当您编译打字稿时,它会搜索路径(要包含的.ts文件的路径)。但是,最终的编译后的.js文件不会替换路径。
解决方案:
所以本质上,tsconfig的一部分看起来像这样
"baseUrl": "./app",
"paths" : {
"@GameInstance" : ["model/game/GameInstance"],
"@Map" : ["model/game/map/Map"],
"@MapCreator" : ["model/game/map/creator/MapCreator"],
"@GameLoop" : ["model/game/GameLoop"],
"@Point" : ["model/other/math/geometry/Point"],
"@Rectangle" : ["model/other/math/geometry/Rectangle"],
"@Functions" : ["model/other/Functions"]
}
并考虑Rectangle.ts文件
import { Point } from '@Point';
import { Vector } from '@Vector';
/**
* Represents a rectangle.
* You can query it for collisions or whether rectangles are touching
*/
export class Rectangle {
//more code
Rectangle.ts在其中
./src/app/model/other/math/geometry/Rectangle/Rectangle.ts
我们跑
tsc
它将编译我们设置的所有.ts文件。在那里,路径将在运行时替换,如果出现错误,请运行
tsc --traceResolution > tmp && gedit tmp
并搜索未定义路径包括的字段。您将能够看到替换日志
然后我们剩下Rectangle.js(已构建)
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var _Point_1 = require("@Point");
//more code
如您所见,@ Point将不存在,我们无法运行这样的节点应用程序。
但是,为此,我创建了一个脚本,该脚本以递归方式搜索js文件并通过先移至根然后移至目标路径来替换令牌。
有requirePaths.data文件,您可以在其中定义令牌和路径。
'@GameLoop' : "./app/model/game/GameLoop"
'@GameInstance' : "./app/model/game/GameInstance"
"@Map" : "./app/model/game/map/Map"
"@MapCreator" : "./app/model/game/map/creator/MapCreator"
"@Point" : "./app/model/other/math/geometry/Point"
"@Rectangle" : "./app/model/other/math/geometry/Point"
现在,这不是通用的,这只是我的热门脚本。请注意,该结构为
src
|-app
| |-model
-build
|-src
|-app
|-model
|-test
从技术上讲,src中的app / model ...结构只是复制到src / build中 tsc从/ src / app获取源代码并进行编译。编译结果在/ src / build
中然后,有一个replacePathsInJS.sh脚本。 这扫描了构建路径,并且每当找到标记@Rectangle时,它将替换它(下面有更多说明...)代码:
#!/bin/bash
function testreqLevel()
{
local srcPath="$1"
local replacingIn="$2"
local expectedLevel=$3
getPathLevel "$replacingIn"
local res=$?
if [ ! $res -eq $expectedLevel ]; then
echo "[-] test $srcPath , $replacingIn FAILED. ($res != $expectedLevel)"
fi
}
function assertreqPath()
{
local input="$1"
local expected="$2"
if [ ! "$input" = "$expected" ]; then
echo "[-] test $expected FAILED"
echo "computed: $input"
echo "expected: $expected"
fi
}
function testGetPathLevel()
{
testreqLevel "./build/src" "./build/src/app/model/game/GameObject.js" 4
testreqLevel "./build/src" "./build/src/file.js" 1
testreqLevel "./build/src" "./build/src/app/model/game/GameObject.js" 4
}
function testGetPathToRoot()
{
local path=$(getPathToRoot "./build/src" "./build/src/app/model/game/GameObject.js")
assertreqPath "$path" "../../../../../"
path=$(getPathToRoot "./" "./server.js")
assertreqPath "$path" "./"
path=$(getPathToRoot "./" "./app/model/game/GameInstance.js")
assertreqPath "$path" "../../../"
}
function err()
{
echo "[-] $1"
}
function getPathLevel()
{
#get rid of starting ./
local input=$(echo "$1" | sed "s/^\.\///")
local contains=$(echo "$input" | grep '/')
if [ -z "$contains" ]; then
return 0
fi
#echo "$input"
local slashInput=$(echo "$input" | awk -F/ '{print NF-1}')
return $(($slashInput - 1))
}
#given ROOT, and PATH, returns a path P such as being in directory PATH, from there using P we get to root
#example:
#ROOT=./src
#PATH=./src/model/game/objects/a.js
#returns ../../
function getPathToRoot()
{
local root="$1"
local input="$2"
getPathLevel "$input"
local level=$?
if [ $level -eq 0 ]; then
echo "./"
return 0
fi
for ((i=1; i <= level + 1; i++)); do
echo -n '../'
done
#echo "$root" | sed 's/^\.\///'
}
function parseData()
{
echo "**************"
echo "**************"
local data="$1"
let lineNum=1
while read -r line; do
parseLine "$line" $lineNum
if [ $? -eq 1 ]; then
return 1
fi
let lineNum++
done <<< "$data"
echo 'Parsing ok'
echo "**************"
echo "**************"
return 0
}
function parseLine()
{
if [[ "$1" =~ ^\#.*$ ]] || [[ "$1" =~ ^\ *$ ]]; then
#comment line
return 0
fi
local line=$(echo "$1" | sed "s/\"/'/g")
let lineNum=$2
local QUOTE=\'
local WORD_IN_QUOTES=$QUOTE[^:$QUOTE]*$QUOTE
if [[ "$line" =~ ^\ *$WORD_IN_QUOTES\ *:\ *$WORD_IN_QUOTES\ *$ ]]; then
# valid key : value pair
local key=$(echo "$line" | awk -F: '{print $1}' | sed 's/^ *//g' \
| sed 's/ *$//g' | sed 's/\//\\\//g' | sed "s/'//g" | sed "s/\./\\\./g")
local val=$(echo "$line" | awk -F: '{print $2}' | sed 's/^ *//g' \
| sed 's/ *$//g' | sed "s/'//g")
echo "[+] Found substitution from '$key' : '$val'"
if [ -z "$REPLACEMENT_KEY_VAL" ]; then
REPLACEMENT_KEY_VAL="$key|$val"
else
REPLACEMENT_KEY_VAL="$REPLACEMENT_KEY_VAL;$key|$val"
fi
else
err "Parse error on line $lineNum"
echo "Expecting lines 'token' : 'value'"
return 1
fi
return 0
}
function replaceInFiles()
{
cd "$WHERE_SUBSTITUTE"
echo "substitution root $WHERE_SUBSTITUTE"
local fileList=`find . -type f -name "*.js" | grep -v "$EXCLUDE"`
echo "$fileList"| while read fname; do
export IFS=";"
echo "Replacing in file '$WHERE_SUBSTITUTE$fname'"
for line in $REPLACEMENT_KEY_VAL; do
local key=`echo "$line" | awk -F\| '{print $1}'`
local val=`echo "$line" | awk -F\| '{print $2}'`
local finalPath=$(getPathToRoot "./" "$fname")"$val"
if [ $VERBOSE -eq 1 ]; then
echo -e "\tsubstitute '$key' => '$val'"
#echo -e "\t$finalPath"
echo -e "\treplacing $key -> $finalPath"
fi
#escape final path for sed
#slashes, dots
finalPath=$(echo "$finalPath" | sed 's/\//\\\//g'| sed 's/\./\\\./g')
if [ $VERBOSE -eq 1 ]; then
echo -e '\t\tsed -i.bak '"s/require(\(.\)$key/require(\1$finalPath/g"\ "$fname"
fi
sed -i.bak "s/require(\(.\)$key\(.\))/require(\1$finalPath\2)/g" "$fname"
done
done
return 0
}
function quit()
{
echo "*************************************"
echo "*****SUBSTITUTING PATHS EXITING******"
echo "*************************************"
echo
exit $1
}
#######################################
CURRENTDIR=`dirname "$(realpath $0)"`
WHERE_SUBSTITUTE='./build/src'
REPLACEMENT_KEY_VAL="";
VERBOSE=0
FILE="$CURRENTDIR/requirePaths.data"
EXCLUDE='./app/view'
if [ "$1" = "-t" ]; then
testGetPathLevel
testGetPathToRoot
echo "[+] tests done"
exit 0
fi
if [ "$1" = "-v" ]; then
VERBOSE=1
fi
echo "*************************************"
echo "********SUBSTITUTING PATHS***********"
echo "*************************************"
if [ ! -f "$FILE" ]; then
err "File $FILE does not exist"
quit 1
fi
DATA=`cat "$FILE"`
parseData "$DATA"
if [ $? -eq 1 ]; then
quit 1
fi
replaceInFiles
quit $?
这似乎令人困惑,但请考虑示例。 我们有Rectangle.js文件。
该脚本从requirePaths.data文件加载一堆输入令牌,在这种情况下,让我们专注于行
"@Point" : "./app/model/other/math/geometry/Point"
脚本从./src运行,并被赋予根目录./src/build/src
脚本执行cd ./src/build/src
执行查找。在那里,它将收到
./model/other/math/geometry/Rectangle/Rectangle.ts
那的绝对路径是
./src/build/src/app/model/other/math/geometry/Rectangle/Rectangle.ts
但是我们现在不在乎绝对路径。
计算路径,例如他从目录获取的路径 会产生这样的结果
./../../../../
他希望在哪里从目录获取
/src/build/app/model/other/math/geometry/Rectangle
到目录
/src/build/app
然后,在该字符串后面,添加数据文件提供的路径
./../../../.././app/model/other/math/geometry/Point
因此,对文件Rectangle.js(在BUILD文件夹中的某个位置)的最终替代是
之前
require("@Point")
之后
require("./../../../.././app/model/other/math/geometry/Point")
这很糟糕,但是我们无论如何都不关心js中的内容。最主要的是它有效。
缺点
您不能将其与代码监视器结合使用。监视tsc,然后在完成代码更改后自动进行tsc编译,然后自动运行shell路径替换,然后对最终的js文件进行tun nodeJS调整,但是由于某种原因,然后用sh脚本替换路径,监视软件认为是代码更改(不知道为什么,它已将其构建从监视器中排除)并再次编译。因此,您生成了无限循环
您必须手动,分步进行编译,或者仅在tsc编译时使用Monitor。当您编写一些代码时,请运行替换并测试nodeJS功能。
添加新的Class Food时,必须为其定义一个令牌(@Food),并在两个位置(tsconfig)定义文件路径,并输入Shell脚本
您可以使整个编译过程更长。坦白地说,tsc会花费大部分时间,bash脚本并没有那么费时。...
使用mocha进行测试时,您必须再次进行逐步编译,完成后,在最终js文件上方运行mocha。但是为此,您可以编写脚本....
某些人通常只替换@app或某些目录。这样做的问题是,每当您移动源文件时,都必须进行很多更改...
好的一面