Ionic 2 меняет имя main.js (настройка выходного имени файла webpack.js)

У нас есть приложение Ionic 2, которое развернуто как в сети, так и в Интернете. При строительстве я использую npm run build --prod --release, Это просто обертывания ionic build.

Я пытаюсь обновить наш процесс сборки, чтобы иметь возможность заменить по умолчанию main.js. то есть в index.html

Поэтому я хочу иметь возможность изменить этот файл с:

<script src="build/main.js"></script>

с (автоматически сгенерированный хеш)

<script src="build/main.7b297e8f7d1c2760a1bc.js"></script>

Шаг 1 - создать файл. Мне удалось успешно сгенерировать нужный файл каждой сборки с помощью параметра вывода web.filename.

module.exports = {
   entry: [process.env.IONIC_APP_ENTRY_POINT, './web.config', './src/ai.min.js'],
   output: {
    path: '{{BUILD}}',
    filename: '[name].[chunkhash].js',

Когда я собираю, я вижу, что он правильно генерирует исходный файл, но вскоре после завершения ionic build не удается с сообщением о невозможности найти build/main.js. Это было исходное имя файла, поэтому я думаю, что мне нужно как-то сообщить, что ionic меняет имя файла main.js.

Ошибка:

[11:00:32] build prod failed: ENOENT: no such file or directory, open '/Users/work/client/www/build/main.js' [11:00:32] ionic-app-script task: "build" [11:00:32] Error: ENOENT: no such file or directory, open '/Users/work/client/www/build/main.js'

Я не уверен, как обновить ionic build чтобы он знал, как искать динамически генерируемое имя файла main.js.

3 ответа

Решение

РЕДАКТИРОВАТЬ

Гораздо более простое решение, которое с гораздо меньшей вероятностью сломается, когда ionic будет иметь большие обновления.

https://gist.github.com/haydenbr/7df417a8678efc404c820c61b6ffdd24


Так что кеш разорится с ионным. Это хакерское решение, но пока оно работает. Проблема в том, что система ионных сборок иногда слишком много абстрагируется. Еще в октябре был задан вопрос о том, существует ли способ реализовать очистку кэша. Ионная команда ответила, что они могли бы рассмотреть это в будущем, но с тех пор там не было никакой активности. Вот проблема GitHub.

Поэтому я покажу все изменения в config и package.json для webpack, а затем объясню все.

Раздел config вашего package.json должен выглядеть следующим образом.

  "config": {
    "ionic_webpack": "./webpack.config.js",
    "ionic_source_map_type": "source-map",
    "ionic_uglifyjs": "./www/uglifyjs.config.json"
  }

Для вашей конфигурации веб-пакета ваши вход и выход могут оставаться неизменными. Убедитесь, что вам потребуются следующие модули, а затем вы захотите добавить следующие плагины:

var path = require('path'),
    fs = require('fs'),
    ManifestPlugin = require('webpack-manifest-plugin'),
    HtmlWebpackPlugin = require('html-webpack-plugin');

...

plugins: [
  new HtmlWebpackPlugin({
    filename: './../index.html',
    inject: 'body',
    template: './src/index.html',
    title: 'My App',
  }),
  new ManifestPlugin(),
  updateFileName
]

где updateFileName как следует

function updateFileName() {
  this.plugin("done", function() {
  var manifest = require(process.env.IONIC_BUILD_DIR + '/manifest.json'),
      fileName = process.env.IONIC_OUTPUT_JS_FILE_NAME;

    updateUglifyConfig(fileName, manifest);

    process.env.IONIC_OUTPUT_JS_FILE_NAME = manifest[fileName];
  });
}

function updateUglifyConfig(fileName, manifest) {
  var uglifyConfig = {
    sourceFile: manifest[fileName],
    destFileName: manifest[fileName],
    inSourceMap: manifest[fileName + '.map'],
    outSourceMap: manifest[fileName + '.map'],
    mangle: true,
    compress: true,
    comments: true
  };

  // we're writing this to www because it's specific to the current
  // build and we don't want to commit it
  fs.writeFileSync(
    path.join(__dirname, 'www', 'uglifyjs.config.json'),
    JSON.stringify(uglifyConfig, null, 4)
  );
}

Так что на самом деле здесь происходит? Во-первых, в файле package.json нам нужно сгенерировать новый конфигурационный файл uglify для процесса ионной сборки. Вы можете изменить имя файла в середине сборки и до тех пор, пока вы назначаете новое имя process.env.IONIC_OUTPUT_JS_FILE_NAME тогда остальная часть сборки будет работать нормально, за исключением того, что шаг uglify по-прежнему будет искать имя по умолчанию, main.js, Мы увидим, как мы создадим это ниже.

Теперь для трех плагинов мы добавляем.

Первый делает немного магии. Это действительно настраивается. Как это установлено, он берет по умолчанию index.html, устанавливает заголовок, вставляет <script> тег для вывода JavaScript, а затем записать его туда, где вы указали в свойстве filename. Если вы используете файл index.html по умолчанию, который поставляется из ионного начального приложения, то все, что вам нужно сделать, это избавиться от <script src="build/main.js"></script> и он автоматически вставит в них новую ссылку для имени файла с хешем в нем. Документы здесь.

Следующий плагин выводит для нас файл манифеста, чтобы мы могли знать, какое имя файла у хэша. По умолчанию он выводит его в www/build/, Документы здесь.

Следующий плагин - это то, что назначает новое имя файла для process.env.IONIC_OUTPUT_JS_FILE_NAME и генерирует новую конфигурацию uglify для нас. В основном мы берем выдаваемый манифест, записываем новую конфигурацию uglify в каталог www, а затем присваиваем имя нового файла из того, что мы получили из манифеста.

Так что вот и все. Если вы не хотите, чтобы очистка кэша выполнялась с помощью dev, сохраните плагин html, избавьтесь от двух других, а затем измените имя выходного файла обратно на process.env.IONIC_OUTPUT_JS_FILE_NAME, Если вы сделаете это, вам не нужно ссылаться на основной файл JS в вашем src/index.html совсем. Это будет вставлено, будь то бегущий разработчик или прод. Чтобы узнать больше об использовании различных настроек веб-пакетов для разных сред, проверьте это.

ОБНОВИТЬ:

Для ионной 3:

  1. Убедитесь, что у вас есть эти настройки в compilerOptions вашей tsconfig:

"module": "es2015", "target": "es5"

  1. npm i cheerio --save-dev
  2. в вашем конфиге добавьте var cheerio = require('cheerio')
  3. Избавьтесь от плагина Webpack Manifest.
  4. + Изменить updateFileName к следующему:

    function updateFileName() {
      this.plugin("done", function(stats) {
        var buildOutput = stats.toJson()['assetsByChunkName']['main'],
            fileName = process.env.IONIC_OUTPUT_JS_FILE_NAME,
            manifest = {
              [fileName]: buildOutput[0],
              [fileName + '.map']: buildOutput[1]
            };
    
        updateUglifyConfig(fileName, manifest);
    
        process.env.IONIC_OUTPUT_JS_FILE_NAME = manifest[fileName];
        console.log('IONIC_OUTPUT_JS_FILE_NAME', process.env.IONIC_OUTPUT_JS_FILE_NAME);
      });
    }
    
  5. Избавьтесь от плагина Html Webpack

  6. Вместо плагина html поместите следующую функцию в массив плагинов в конфигурации вашего веб-пакета:

    function updateIndexHTML() {
    this.plugin("done", function(stats) {
    var buildOutput = stats.toJson()['assetsByChunkName']['main'],
        outputFileName = buildOutput[0],
        currentIndexHTML = fs.readFileSync(
          path.join(__dirname, 'src', 'index.html'),
          { encoding: 'utf8' }
        ),
        $ = cheerio.load(currentIndexHTML);
    
    $('body').append(`<script src="build/${outputFileName}"></script>`);
    
    fs.writeFileSync(
      path.join(process.env.IONIC_WWW_DIR, 'index.html'),
      $.html()
    );
      });
    }
    

Я нашел лучшее решение этой проблемы из ветки ионного форума ( https://forum.ionicframework.com/t/file-revisions/75028/2, автор aszmyd), которая решает проблему хэширования имени файла main.css. также. Я сделал небольшие изменения в скрипте, так как у меня нет oauth.html или kajam.js.

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

(Несомненно, кто-то найдет ошибку в том, как я публикую это, но это было очень полезно для меня, и я надеюсь, что для других. Я не могу представить себе наличие веб-приложения без полной очистки кэша для всех файлов css и js.)

Чтобы запустить это, просто добавьте:

node <the-file-name.js>

в вашей сборке, после завершения сборки ионного скрипта.

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

var md5File = require('md5-file'),
    fs = require('fs');

/**
 * This script renames files inside platforms/browser/www/ folder and updates their references in html files like index.html
 * The mechanism is for improve caching. So file like `main.js` will be renamed to `main.[FILE-MD5-HASH].js` and its references
 * in html files will be updated.
 */
var buildFolder = 'www/';
var assetsFolder = buildFolder + 'build/';

var jsFiles = [
    'main'
];
var cssFiles = [
    'main'
];
var htmlFilesToUpdate = [
    'index.html'
];
var replacements = [];

jsFiles.forEach(function (file) {
    var hash = md5File.sync(assetsFolder + file + '.js');
    renameFile(file + '.js', file + '.' + hash + '.js');
});

cssFiles.forEach(function (file) {
    var hash = md5File.sync(assetsFolder + file + '.css');
    renameFile(file + '.css', file + '.' + hash + '.css');
});
htmlFilesToUpdate.forEach(function (htmlFile) {
    console.log('Update "' + htmlFile + '" with new file revisions.');
    console.log('Replacements: ' + JSON.stringify(replacements));
    replacements.forEach(function (replacementObject) {
        replaceInFile(buildFolder + htmlFile, replacementObject.from, replacementObject.to);
    });
});

function renameFile(input, output) {
    console.log('Rename "' + input + '" to "' + output + '"');
    fs.rename(assetsFolder + input, assetsFolder + output);
    if (fs.existsSync(assetsFolder + input + '.map')) {
        console.log('Rename "' + input + '.map" to "' + output + '.map"');
        fs.rename(assetsFolder + input + '.map', assetsFolder + output + '.map');
    }
    replacements.push({from: input, to: output});
}

function replaceInFile(file, regex, replacement) {
    var fileContents = fs.readFileSync(file, 'utf-8');
    fs.writeFileSync(file, fileContents.replace(regex, replacement), 'utf8');
}

Это решение отлично работает с Ionic 2.x & 3.x

#!/usr/bin/env node

var fs = require('fs'),
    path = require('path'),
    cheerio = require('cheerio'),
    revHash = require('rev-hash');

/**
 *
 * @param string fileName
 * @returns string
 */
function hashFile(file) {

    // Get file name
    var fileName = file.replace(/\.[^/.]+$/, "");
    // Get file extension
    var re = /(?:\.([^.]+))?$/;
    var fileExtension = re.exec(file)[1];

    var filePath = path.join(buildDir, file);
    var fileHash = revHash(fs.readFileSync(filePath));
    var fileNewName = `${fileName}.${fileHash}.${fileExtension}`;
    var fileNewPath = path.join(buildDir, fileNewName);
    var fileNewRelativePath = path.join('build', fileNewName);
    //Rename file
    console.log("cache-busting.js:hashFile:Renaming " + filePath + " to " + fileNewPath);
    fs.renameSync(filePath, fileNewPath);

    return fileNewRelativePath;
}


var rootDir = path.resolve(__dirname);
var wwwRootDir = path.resolve(rootDir, 'www');
var buildDir = path.join(wwwRootDir, 'build');
var indexPath = path.join(wwwRootDir, 'index.html');
$ = cheerio.load(fs.readFileSync(indexPath, 'utf-8'));

$('head link[href="build/main.css"]').attr('href', hashFile('main.css'));
$('body script[src="build/main.js"]').attr('src', hashFile('main.js'));
$('body script[src="build/polyfills.js"]').attr('src', hashFile('polyfills.js'));
$('body script[src="build/vendor.js"]').attr('src', hashFile('vendor.js'));

fs.writeFileSync(indexPath, $.html());
Другие вопросы по тегам