Webpack не разрешает расширения из сторонних библиотек

Я разрабатываю 2 библиотеки Angular 2:

  1. CarbonLDP-верстак
  2. углеродлдп-панель

Проект2 (панель):

Набор экспортируемых компонентов и модулей. Эти компоненты имеют свои собственные шаблоны и файлы scss. Библиотека написана с помощью Typescript, но файлы dist компилируются, поэтому библиотека поставляется в виде пакета npm со своими собственными файлами .js с их файлами .metadata.json и файлами .d.ts.

Проект1 (Workbench)

Является основным проектом. Он использует Project2, поэтому он может работать. Этот проект использует Webpack для сборки окончательного приложения. Дело в том, что я не могу заставить webpack работать с Project2 (Panel), потому что основной проект написан на машинописном языке, а панель импортируется только с файлами javascript. Ошибки, которые я получаю от webapack, заключаются в том, что ВСЕ файлы шаблонов из Project2 не могут быть найдены. Смотри ниже:

введите здесь описание изображения

Вот как окончательный файл app.js объединяет библиотеку Project2. Обратите внимание, как компонент заголовка, полученный из Project2, импортирует файлы шаблонов:

введите здесь описание изображения

Обратите внимание, как webpack импортирует файлы шаблонов и стилей из main проекта Project1 (написанного с помощью ts):

введите здесь описание изображения

Вот как я определил правила разрешения расширений в моем файле webpack.config.js:

entry: {
    "polyfills": "./src/polyfills.ts",
    "app": "./src/main.ts"
},

resolve: {
    extensions: [ ".ts", ".js" ],
    alias: {
        "app": helpers.root( "src", "app" ),
        "jquery": "jquery/src/jquery",
        "semantic-ui": helpers.root( "src/semantic/dist" ),
    },
    modules: [ helpers.root( "node_modules" ) ]
},

module: {
    rules: [
            {
                test: /\.ts$/,
                use: [ "awesome-typescript-loader", "angular2-template-loader", "angular-router-loader" ]
            },
            {
                test: /\.html$/,
                use: "raw-loader",
                exclude: [ helpers.root( "src/index.html" ) ]
            },
            {
                test: /\.(png|jpe?g|gif|svg|woff|woff2|ttf|eot|ico)$/,
                use: "file-loader?name=assets/[name].[hash].[ext]"
            },
            {
                test: /\.s?css$/,
                use: [ "raw-loader", "sass-loader" ]
            },
        ]
    },

Возникает вопрос... Как заставить Webpack также компилировать импорт моей библиотеки angular js?


person Alvaro Contreras    schedule 15.03.2017    source источник


Ответы (1)


По моему опыту, angular2-template-loader не будет копаться в node_modules, поэтому библиотекам внутри node_modules, которые могут зависеть от него, не повезет.

Способ, которым я обошел это, заключается в том, чтобы встроить внешние шаблоны и файлы css как часть шага сборки библиотеки, и я в значительной степени основывался на том, как angular material 2 публиковал свою библиотеку. Тогда веб-пакету — на уровне потребляющего приложения — не нужно беспокоиться о ваших шаблонах или таблицах стилей, поскольку они уже готовы.

Чтобы не использовать ссылку, вот их сценарий, и я не беру на себя ответственность за него:

#!/usr/bin/env node
'use strict';

const fs = require('fs');
const path = require('path');
const glob = require('glob');

/**
 * Simple Promiseify function that takes a Node API and return a version that supports promises.
 * We use promises instead of synchronized functions to make the process less I/O bound and
 * faster. It also simplify the code.
 */
function promiseify(fn) {
  return function() {
    const args = [].slice.call(arguments, 0);
    return new Promise((resolve, reject) => {
      fn.apply(this, args.concat([function (err, value) {
        if (err) {
          reject(err);
        } else {
          resolve(value);
        }
      }]));
    });
  };
}

const readFile = promiseify(fs.readFile);
const writeFile = promiseify(fs.writeFile);


function inlineResources(globs) {
  if (typeof globs == 'string') {
    globs = [globs];
  }

  /**
   * For every argument, inline the templates and styles under it and write the new file.
   */
  return Promise.all(globs.map(pattern => {
    if (pattern.indexOf('*') < 0) {
      // Argument is a directory target, add glob patterns to include every files.
      pattern = path.join(pattern, '**', '*');
    }

    const files = glob.sync(pattern, {})
      .filter(name => /\.js$/.test(name));  // Matches only JavaScript files.

    // Generate all files content with inlined templates.
    return Promise.all(files.map(filePath => {
      return readFile(filePath, 'utf-8')
        .then(content => inlineResourcesFromString(content, url => {
          return path.join(path.dirname(filePath), url);
        }))
        .then(content => writeFile(filePath, content))
        .catch(err => {
          console.error('An error occurred: ', err);
        });
    }));
  }));
}

/**
 * Inline resources from a string content.
 * @param content {string} The source file's content.
 * @param urlResolver {Function} A resolver that takes a URL and return a path.
 * @returns {string} The content with resources inlined.
 */
function inlineResourcesFromString(content, urlResolver) {
  // Curry through the inlining functions.
  return [
    inlineTemplate,
    inlineStyle,
    removeModuleId
  ].reduce((content, fn) => fn(content, urlResolver), content);
}

if (require.main === module) {
  inlineResources(process.argv.slice(2));
}


/**
 * Inline the templates for a source file. Simply search for instances of `templateUrl: ...` and
 * replace with `template: ...` (with the content of the file included).
 * @param content {string} The source file's content.
 * @param urlResolver {Function} A resolver that takes a URL and return a path.
 * @return {string} The content with all templates inlined.
 */
function inlineTemplate(content, urlResolver) {
  return content.replace(/templateUrl:\s*'([^']+?\.html)'/g, function(m, templateUrl) {
    const templateFile = urlResolver(templateUrl);
    const templateContent = fs.readFileSync(templateFile, 'utf-8');
    const shortenedTemplate = templateContent
      .replace(/([\n\r]\s*)+/gm, ' ')
      .replace(/"/g, '\\"');
    return `template: "${shortenedTemplate}"`;
  });
}


/**
 * Inline the styles for a source file. Simply search for instances of `styleUrls: [...]` and
 * replace with `styles: [...]` (with the content of the file included).
 * @param urlResolver {Function} A resolver that takes a URL and return a path.
 * @param content {string} The source file's content.
 * @return {string} The content with all styles inlined.
 */
function inlineStyle(content, urlResolver) {
  return content.replace(/styleUrls:\s*(\[[\s\S]*?\])/gm, function(m, styleUrls) {
    const urls = eval(styleUrls);
    return 'styles: ['
      + urls.map(styleUrl => {
          const styleFile = urlResolver(styleUrl);
          const styleContent = fs.readFileSync(styleFile, 'utf-8');
          const shortenedStyle = styleContent
            .replace(/([\n\r]\s*)+/gm, ' ')
            .replace(/"/g, '\\"');
          return `"${shortenedStyle}"`;
        })
        .join(',\n')
      + ']';
  });
}


/**
 * Remove every mention of `moduleId: module.id`.
 * @param content {string} The source file's content.
 * @returns {string} The content with all moduleId: mentions removed.
 */
function removeModuleId(content) {
  return content.replace(/\s*moduleId:\s*module\.id\s*,?\s*/gm, '');
}


module.exports = inlineResources;
module.exports.inlineResourcesFromString = inlineResourcesFromString;

Они используют это как часть задачи gulp, я переработал свой, чтобы он был просто скриптом узла.

Что я делаю в своей сборке:

  • create a dist folder from my src folder (so html and css files remain relative), and then from within dist:
    • do any kind of postcss processing here if required (with postcss the best option is to use the replace flag to maintain folder structure)
    • запустить инлайнер (скрипт выше)
    • удалите файлы html и css (они больше не нужны)
    • транспилировать с помощью ngc

На первом этапе я начал с использования промежуточной папки, а затем перенес ее в папку dist, но ngc не согласовывался с наличием outDir.

Потребовалось некоторое время, чтобы понять это, так что, надеюсь, я смогу сэкономить вам немного времени.

person nikolasleblanc    schedule 15.03.2017
comment
Спасибо, мужик!!!!! Это на самом деле то, что я искал! Способ включения содержимого файлов из другой библиотеки. Это ответ. - person Alvaro Contreras; 16.03.2017
comment
Кроме того, вы случайно не знаете, может ли angular-router-loader сделать то же самое? Эта библиотека включает маршруты с ленивой загрузкой, и на самом деле я получаю тот же результат. 404 при попытке загрузить файл module.ts ... При проверке связанных файлов Webpack правильно изменил маршруты loadChildren, но с библиотекой он оставил путь нетронутым, старую простую строку. Я имею в виду, это нелепый человек, нельзя использовать полную угловую библиотеку третьей части с веб-пакетом. - person Alvaro Contreras; 16.03.2017
comment
На самом деле, это действительно хороший вопрос. Я понятия не имею, еще не приходилось делать это с моим проектом, поэтому не сталкивался с этим. Возможно, опубликуйте это как еще один вопрос SO, и я тем временем подумаю об этом. - person nikolasleblanc; 16.03.2017