Упаковка нескольких проектов Typescript в зависимости от одного и того же локального модуля

Я работаю над набором расширений VSTS. Каждое расширение - это собственный маленький Node-проект со своим package.json и свой node_modules папка. Структура папок выглядит следующим образом:

 - MyExtension
   - package.json // containing all dev-dependencies
   - tslint.json
   - Tasks
     - tsconfig.json
     - Common
       - common.ts    // containing functioanlity shared across tasks
       - package.json // containing all runtime dependencies for all projects
     - My1stTask
       - package.json // containing all prod-dependencies
       - task.ts      // containing task implementation
     - ...
     - ...
     - My6thTask
       - package.json // containing all prod-dependencies
       - task.ts      // containing task implementation

Способ построения задач VSTS заключается в том, что они должны быть полностью автономными. Я исправил это до сих пор, скопировав содержимое Common проецировать в каждую задачу, а затем запустить tsc преобразовать их всех в JavaScript.

Это неплохо, но требует постоянного копирования содержимого Common, чтобы что-то проверить.

Я попытался использовать локальные ссылки на файлы, добавил зависимость в package.json каждой задачи file:../common, который работает во время разработки, но это не приводит к тому, что общий модуль становится частью задачи после генерации расширения.

Мой фон не в разработке узлов, а в C#. Я искал по всему и не нашел решения, которое хорошо работает с VSTS-расширениями.

  • npm pack похоже, не работает, так как расширение ожидает, что все файлы будут там.
  • package.json/bundleDependencies выглядит многообещающе, но не связывает локальную ссылку на файл.
  • ///<reference path="../common/common.ts"/> прекрасно работает для редактирования, но все еще не может работать после создания расширения.
  • ссылка на проект с prepend не работает, для задач сборки требуется модуль разрешения commonjs. Система и AMD не могут загружать модули. Prepend работает только с последним.

Есть ли способ, которым я могу сделать эту работу "без проблем", не взирая на недовольство или хрюканье, и просто заставляя каждую MyXthTask иметь копию локального общего модуля в своем node_modules папка?

2 ответа

Я попробовал подход @matt-mccutchen, но, к сожалению, я не смог заставить его работать с задачами сборки VSTS из-за того, что эти задачи требуют commonjs:

"compilerOptions": {
  "module": "commonjs",
  "target": "es6", 

Но я нашел решение, которое работает для меня.

в Tasks папка, которую я добавил tsconfig.json который определяет мои настройки по умолчанию и включает файлы из общей библиотеки:

{
  "compileOnSave": true,
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "sourceMap": true,
    "strict": false,
    "strictNullChecks": false,
    "removeComments": true
  },
  "files": [
    "./Common/uuidv5.d.ts",
    "./Common/Common.ts"
  ]
}

Затем в каждом задании я создал tsconfig.json который устанавливает выходную папку в текущую папку для этого проекта и которая наследует от tsconfig.json в Tasks папка:

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
      "outDir": "./", 
      "sourceRoot": "./"
  },
  "files": [
      "InstallExtension.ts"
  ]
}

Это приводит к:

 - MyExtension
   - package.json // containing all dev-dependencies
   - tslint.json
   - Tasks
     - tsconfig.json   // Including Common by default
     - Common
       - common.ts     // containing functionality shared across tasks
       - package.json  // containing all runtime dependencies for Common
       - tsconfig.json // containing build configuration for just the common files, inherits from ..\Task\tsconfig.json
     - My1stTask
       - package.json  // containing all prod-dependencies for the task
       - task.ts       // containing task implementation
       - tsconfig.json // containing build configuration for the task, inherits from ..\Task\tsconfig.json
     - ...
     - ...
     - My6thTask
       - package.json // containing all prod-dependencies
       - task.ts      // containing task implementation
       - tsconfig.json // containing build configuration for the task, inherits from ..\Task\tsconfig.json

При составлении задачи следующее

     - My6thTask
       - Common
         - Common.js   // Compiled common
       - My6thTask
         - task.js     // Compiled task
       - package.json  // containing all prod-dependencies
       - task.ts       // containing task implementation
       - task.json     // defining the task UI
       - tsconfig.json // containing build configuration for the task

Единственное, что я должен был добавить к task.ts является следующим:

///<reference path="../Common/Common.ts"/>
import * as common from "../Common/Common";

И изменить обработчик выполнения в task.json, чтобы он указывал на новое местоположение:

  "execution": {
    "Node": {
      "target": "InstallExtension/InstallExtension.js", // was: InstallExtension.js
      "argumentFormat": ""
    }
  }

И все вроде нормально:D. В сочетании с использованием glob-exec Я смог сократить время сборки до менее минуты при чистом сборке:

"initdev:npm": "npm install & glob-exec --parallel --foreach \"Tasks/*/tsconfig.json\" -- \"cd {{file.dir}} && npm install\"",
"compile:tasks": "glob-exec \"Tasks/*/tsconfig.json\" -- \"tsc -b {{files.join(' ')}}\"",
"lint:tasks": "glob-exec --parallel --foreach \"Tasks/*/tsconfig.json\" -- \"tslint -p {{file}}\"",

Вы можете быть в состоянии использовать ссылки проекта с prepend вариант, если вы удовлетворены тем, что генерируете один файл в качестве вывода для каждой задачи (и используете совместимый загрузчик модулей) вместо генерации отдельных модулей.

Если вам нужен многофайловый вывод, см. Этот вопрос для предложения TypeScript и возможного обходного пути.

Ссылки на проекты в моем случае не работают, поэтому я использовал webpack с ts-loader для создания своих проектов.

Поместите весь ts-код в один корень с одним tsconfig, чтобы он был таким.

ts
 -core
 -plugin1
 -plugin2

Начиная с 3.10 webpack допускает несколько конфигураций вывода. Таким образом, мы могли бы использовать один такой файл конфигурации.

const path = require("path");

const commonTsRule = {
  test: /\.tsx?$/,
  use: "ts-loader",
  exclude: /node_modules/,
};

const commonConfig = {
  devtool: "inline-source-map",
  mode: "development",
  resolve: {
    extensions: [".tsx", ".ts", ".js"],
  },
};

module.exports = [
  {
    ...commonConfig,
    entry: `${path.resolve(__dirname, "ts")}/plugin1/index.ts`,
    output: {
      filename: "bundle.js",
      path: path.resolve(__dirname, "build/plugin1/js"),
    },
    module: {
      rules: [
        {
          ...commonTsRule,
          // here you can customize rule if required
        },
      ],
    },
  },
  {
    ...commonConfig,
    entry: `${path.resolve(__dirname, "ts")}/plugin2/index.ts`,
    output: {
      filename: "bundle.js",
      path: path.resolve(__dirname, "build/plugin2/js"),
    },
    module: {
      rules: [
        {
          ...commonTsRule
          // here you can customize rule if required
        },
      ],
    },
  },
];

Таким образом, в конечном результате у вас будет каждый проект, построенный из собственной точки входа и в свое собственное место назначения.