Ionic 2 изменить имя main.js (параметр webpack.js output.filename)

У нас есть приложение 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 — сгенерировать файл. Я смог успешно создать правильный файл для каждой сборки, используя параметр webpack output.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.


person Nix    schedule 14.04.2017    source источник


Ответы (3)


ИЗМЕНИТЬ

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

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


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

Итак, я покажу все изменения в конфигурации webpack и package.json, а затем все объясню.

Раздел конфигурации вашего 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 для процесса сборки ionic. Вы можете изменить имя файла в середине сборки, и пока вы назначаете новое имя process.env.IONIC_OUTPUT_JS_FILE_NAME, остальная часть сборки будет работать нормально, за исключением того, что шаг uglify будет по-прежнему искать имя по умолчанию, main.js. Мы увидим, как мы сгенерируем это ниже.

Теперь о трех подключаемых модулях, которые мы добавляем.

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

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

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

Вот и все. Если вы не хотите, чтобы кеш очищался для разработчиков, сохраните плагин 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()
    );
      });
    }
    
person Hayden Braxton    schedule 15.04.2017
comment
Я уже разместил это в нашем проекте @Nix, так что вы можете проверить это там :). - person Hayden Braxton; 15.04.2017
comment
Привет, используя ваш код Ionic v3, он строится нормально, но не хеширует файлы. Что мне нужно изменить, чтобы хешировать файлы? я попытался установить process.env.IONIC_OUTPUT_JS_FILE_NAME в методе updateFileName, но всегда использую main.js - person tito.icreativos; 26.06.2017
comment
Я посмотрю сегодня вечером и удостоверюсь, что я обновил все правильно. Я думаю, было бы полезно просто опубликовать полную конфигурацию веб-пакета для этого. Прежде всего, убедитесь, что в вашей конфигурации есть output: { path: '{{BUILD}}', filename: '[name].[chunkhash].js' }. - person Hayden Braxton; 27.06.2017
comment
если поставить output: { path: '{{BUILD}}', filename: '[name].[chunkhash].js' }, режим разработки работать не будет. Это ошибка Uncaught reflect-metadata shim is required when using class decorators в консоли браузера. Но если только строить для режима производства. Затем его рабочая + ленивая нагрузка. - person ChokYeeFan; 28.09.2017
comment
После того, как приложение уже загружено, снова нажмите «Обновить», это вызовет проблему Uncaught reflect-metadata shim is required when using class decorators. Любые идеи? - person ChokYeeFan; 03.10.2017

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

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

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

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

node <the-file-name.js>

в вашей сборке после завершения сборки ionic script.

#!/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');
}
person Community    schedule 12.07.2017
comment
Поддерживает ли это решение ionic v3 + ленивую загрузку? - person ChokYeeFan; 28.09.2017
comment
Ленивые модули @ChokYeeFan на данный момент 0.js, 1.js, ... Так что это не работает, потому что angular не знает внутренне, что имена файлов изменились, а также ищет решение - person tobika; 28.09.2017

Это решение отлично работает с 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());
person anlijudavid    schedule 01.06.2018