如何使用路径编译器选项

时间:2017-06-21 13:02:07

标签: typescript

我想要使用脚本编译几个文件。这些文件依赖于paths编译选项来解析其模块。由于我想特别针对这些文件,因此我必须将它们提供给tsc(因为我不想为此任务创建一个单独的tsconfig.json目标文件)

我查看了将--path参数传递给tsc的选项,但这是不允许的(error TS6064: Option 'paths' can only be specified in 'tsconfig.json' file.

我可以在使用.ts选项时以某种方式编译特定的paths文件吗?

更新(22-06-17)

根据要求提供一些具体的例子:

tsconfig.json文件中的相关设置如下:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "Components/*": [
        "./src/Components/*"
      ]
    }
  }
}

所以相关部分是路径设置,您可以在其中创建缩写以从某个目录导入文件。因此,您可以导入import {header} from 'Components/header.ts'之类的文件,而不是import {header} from '../../Components/header.ts'

但我需要从命令行编译特定文件。但如果我尝试:

tsc --project tsconfig.json entryFile.ts

它会给我错误:

error TS5042: Option 'project' cannot be mixed with source files on a command line.

如果我尝试向cli提供paths选项,我会收到以下错误:

error TS6064: Option 'paths' can only be specified in 'tsconfig.json' file.

6 个答案:

答案 0 :(得分:11)

在命令行上指定输入文件会使tsconfig.json无效。

  

在命令行中指定输入文件时,将忽略tsconfig.json文件。

来自TypeScript docs

因此,除了将tsconfig.json与文件的正确“包含”(或排除)规范一起使用之外别无选择。好消息是,在相同的文档中你会发现:

  

tsconfig.json文件可以使用extends属性从其他文件继承配置。

所以可以做的是用以下内容覆盖tsconfig的include属性:

{
  "extends": "./tsconfig.json",
  "include": [
      "src/root/index.ts"
  ]
}

所以你可以创建一个脚本:

  1. 使用所需的'include'属性
  2. 创建临时'extends tsconfig'文件
  3. 调用tsc并将--project指定为临时tsconfig
  4. 有人可能会说这很复杂,并且质疑为什么他们决定不正确支持这种情况。但是,问题仍然是为什么只想编译项目的一个文件。在开发时,您可以使用watch(-w)选项来编译更改文件,在构建完整项目时,您可能希望完全编译完整项目。

答案 1 :(得分:3)

我同样需要确保项目将使用lint-staged进行编译。如果允许使用tsconfig.json和文件路径的组合,则可以直接将"tsc"用作lint-staged命令。

作为一种解决方法,我创建了以下帮助脚本scripts/tsc-lint.sh

#!/bin/bash -e

TMP=.tsconfig-lint.json
cat >$TMP <<EOF
{
  "extends": "./tsconfig.json",
  "include": [
EOF
for file in "$@"; do
  echo "    \"$file\"," >> $TMP
done
cat >>$TMP <<EOF
    "unused"
  ]
}
EOF
tsc --project $TMP --skipLibCheck --noEmit

然后,棉绒分阶段的配置包含:

  "lint-staged": {
    "{src,tests}/**/*.ts": [
      "scripts/tsc-lint.sh"
    ]
  },

文件.tsconfig-lint.json已添加到.gitignore

答案 2 :(得分:1)

只是基于@Bram的答案...

在我的项目中,我要做的是两个tsconfig:

project/
   dist/
   docs/
   src/
   package.json
   tsconfig.json
   tsconfig-build.json

所以我有一个tsconfig-build.json(因为我不想在构建中包含docs/

// tsconfig-build.json

{
    "extends": "./tsconfig.json",
    "include": [
        "src/**/*"
    ]
}

然后您可以在package.json中插入一行:

// package.json
  ...

  "scripts": {

    ...

    "build": "tsc --p tsconfig-build.json"
  },

答案 3 :(得分:0)

我正是使用编译器 API 实现了这一点。这是一个(大大)修改 their minimal compiler example 的版本。

import * as ts from 'typescript';
import * as fs from 'fs';
import * as path from 'path';
import * as cp from 'child_process';
import * as dm from '../src-ts/deep-merge-objects';
function compile(fileNames: string[], options: ts.CompilerOptions): void {
  const program = ts.createProgram(fileNames, options);
  const emitResult = program.emit();

  const allDiagnostics = ts
    .getPreEmitDiagnostics(program)
    .concat(emitResult.diagnostics);

  allDiagnostics.forEach((diagnostic) => {
    if (diagnostic.file) {
      const {line, character} = diagnostic.file.getLineAndCharacterOfPosition(
        diagnostic.start!
      );
      const message = ts.flattenDiagnosticMessageText(
        diagnostic.messageText,
        '\n'
      );
      console.log(
        `${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`
      );
    } else {
      console.log(
        ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')
      );
    }
  });

  const exitCode = emitResult.emitSkipped ? 1 : 0;
  console.log(`Process exiting with code '${exitCode}'.`);
  process.exit(exitCode);
}

function main(): void {
  fs.mkdirSync('./dev-out',{recursive:true})
  cp.execSync('cp -r ./testlib-js ./dev-out/');

  const tscfgFilenames = [
    '@tsconfig/node14/tsconfig.json',
    path.join(process.cwd(), 'tsconfig.base.json'),
    path.join(process.cwd(), 'tsconfig.json'),
  ];
  const tscfg = tscfgFilenames.map((fn) => require(fn).compilerOptions);
  const compilerOptions: ts.CompilerOptions = dm.deepMerge(
    dm.deepMergeInnerDedupeArrays,
    ...tscfg
  );

  if (compilerOptions.lib && compilerOptions.lib.length)
    compilerOptions.lib = compilerOptions.lib.map((s) => 'lib.' + s + '.d.ts');
  console.log(JSON.stringify(compilerOptions, null, 2));
  compile(process.argv.slice(2), compilerOptions);
}
try {
  main();
  if (process.exitCode === 1) console.log('compiler produced no output');
} catch (e) {
  console.error(e.message);
  process.exitCode = 2;
}

问题是

  • 读取配置文件。原始示例中未包含此内容。
  • 按顺序合并配置文件。 (如果您使用单个文件,则没有问题)。
  • 转换 compilerOptions.lib 条目
if (compilerOptions.lib && compilerOptions.lib.length)
    compilerOptions.lib = compilerOptions.lib.map((s) => 'lib.' + s + '.d.ts');

例如,“es2020”更改为“lib.es2020.d.ts”。

通用对象合并代码在这里:

// adapted from  adrian-marcelo-gallardo
// https://gist.github.com/ahtcx/0cd94e62691f539160b32ecda18af3d6#gistcomment-3257606
//
type objectType = Record<string, any>;

export const isObject = (obj: unknown): obj is objectType => {
  return <boolean>obj && typeof obj === 'object';
};
export function deepMerge(
  deepMergeInner: (target: objectType, source: objectType) => objectType,
  ...objects: objectType[]
): objectType {
  if (objects.length === 0) return {};
  if (objects.length === 1) return objects[0];
  if (objects.some((object) => !isObject(object))) {
    throw new Error('deepMerge: all values should be of type "object"');
  }
  const target = objects.shift() as objectType;
  //console.log(JSON.stringify(target,null,2))
  let source: objectType;
  while ((source = objects.shift() as objectType)) {
    deepMergeInner(target, source);
    //console.log(JSON.stringify(target,null,2))
  }
  return target;
}

export function deepMergeInnerDedupeArrays(
  target: objectType,
  source: objectType
): objectType {
  function uniquify(a: any[]): any[] {
    return a.filter((v, i) => a.indexOf(v) === i);
  }
  Object.keys(source).forEach((key: string) => {
    const targetValue = target[key];
    const sourceValue = source[key];
    if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
      target[key] = uniquify(targetValue.concat(sourceValue));
    } else if (isObject(targetValue) && Array.isArray(sourceValue)) {
      target[key] = sourceValue;
    } else if (Array.isArray(targetValue) && isObject(sourceValue)) {
      target[key] = sourceValue;
    } else if (isObject(targetValue) && isObject(sourceValue)) {
      target[key] = deepMergeInnerDedupeArrays(
        Object.assign({}, targetValue),
        sourceValue
      );
    } else {
      target[key] = sourceValue;
    }
  });
  return target;
}

另一个是目录结构。除非您使用,否则您可能无法获得与所有文件相同的输出目录 compilerOptions.rootDir。通常:

  "compilerOptions": {
    "outDir": "dev-out",
    "rootDir": "./"
  },

了解rootDir here.

答案 4 :(得分:0)

--noResolve 是这里的关键:https://www.typescriptlang.org/tsconfig/#noResolve

举个例子:

// tsconfig.myFile.json
{
  // "extends": "./tsconfig.json",
  "include": [
    "myFile.ts"
  ],
    "compilerOptions": {
        "noResolve": true
    }
}

$ tsc --project tsconfig.myFile.json

还有一些方法可以包含 --noResolve 和一些输出标志,只需使用命令行即​​可。

答案 5 :(得分:-4)

  

我可以在使用路径选项

时以某种方式编译特定的.ts文件

你不需要路径。只需传入文件,例如

{{1}}