Ionic 2 更改 main.js 的名称(webpack.js output.filename 设置)
Ionic 2 change name of main.js ( webpack.js output.filename setting)
我们有一个 Ionic 2 应用程序,它可以本地部署,也可以部署到网络上。构建时我使用 npm run build --prod --release
。那只是包装 ionic build.
我正在尝试更新我们的构建过程,以便能够换出 index.html
中的默认 main.js.
所以我希望能够更改此文件:
<script src="build/main.js"></script>
与(自动生成的散列)
<script src="build/main.7b297e8f7d1c2760a1bc.js"></script>
第一步是生成文件。通过使用 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 文件名。
编辑
更简单的解决方案,当 ionic 有 manjor 更新时,不太可能破坏。
https://gist.github.com/haydenbr/7df417a8678efc404c820c61b6ffdd24
所以缓存破坏与离子。这是一个骇人听闻的解决方案,但目前有效。问题是离子构建系统有时抽象得太多了。早在 10 月份,就有人询问是否有一种方法可以实现缓存清除。 ionic 团队回应说他们可能会在未来考虑它,但是从那以后就没有任何 activity 了。 Here's the github issue.
因此,我将展示对 webpack 配置和 package.json 的所有更改,然后解释所有内容。
您的 package.json 的配置部分应该如下所示。
"config": {
"ionic_webpack": "./webpack.config.js",
"ionic_source_map_type": "source-map",
"ionic_uglifyjs": "./www/uglifyjs.config.json"
}
对于您的 webpack 配置,您的输入和输出可以保持不变。确保您需要以下模块,然后您需要添加以下插件:
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,设置标题,为 javascript 输出注入一个 <script>
标记,然后将其写入您在文件名中指定的位置 属性。如果您使用的是来自 ionic starter 应用程序的默认 index.html,那么您需要做的就是摆脱 <script src="build/main.js"></script>
,它会自动将新的 link 放入他们用于其中包含哈希的文件名。 Docs here.
下一个插件会为我们输出一个清单文件,这样我们就可以知道文件名和哈希值是什么。默认情况下,它在 www/build/
中输出。 Docs here.
下一个插件是将新文件名分配给 process.env.IONIC_OUTPUT_JS_FILE_NAME 并为我们生成新的 uglify 配置。我们几乎获取了输出的清单,将新的 uglify 配置写入 www 目录,然后根据我们从清单中获得的内容分配新文件名。
差不多就是这样。如果您不想为开发人员破坏缓存,请保留 html 插件,去掉其他两个,然后将输出文件名改回 process.env.IONIC_OUTPUT_JS_FILE_NAME
。如果这样做,您根本不需要在 src/index.html
中引用主 js 文件。无论您的 运行 开发还是生产,它都会被放入。有关针对不同环境使用不同 webpack 设置的更多信息,check this out.
更新:
对于离子 3:
- 确保您在
tsconfig
的 compilerOptions
中有这些设置:
"module": "es2015", "target": "es5"
npm i cheerio --save-dev
- 在你的 webpack 配置中添加
var cheerio = require('cheerio')
- 去掉 Webpack Manifest 插件。
将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);
});
}
去掉 Html Webpack 插件
代替 html 插件,将以下函数放在 webpack 配置的插件数组中:
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()
);
});
}
我从一个 ionic 论坛线程(https://forum.ionicframework.com/t/file-revisions/75028/2,作者 aszmyd)找到了解决此问题的更好方法,该线程也解决了对 main.css 文件名进行哈希处理的问题。我对脚本做了一些小的调整,因为我没有 oauth.html 或 kajam.js。
此解决方案的优势在于它不会尝试拦截离子构建,而只会处理结果。
(毫无疑问,有人会挑剔我 post 的方法,但这对我很有用,我希望对其他人也有用。我无法想象没有完整缓存的网络应用程序-破坏所有 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');
}
此 solution 与 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());
我们有一个 Ionic 2 应用程序,它可以本地部署,也可以部署到网络上。构建时我使用 npm run build --prod --release
。那只是包装 ionic build.
我正在尝试更新我们的构建过程,以便能够换出 index.html
中的默认main.js.
所以我希望能够更改此文件:
<script src="build/main.js"></script>
与(自动生成的散列)
<script src="build/main.7b297e8f7d1c2760a1bc.js"></script>
第一步是生成文件。通过使用 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 文件名。
编辑
更简单的解决方案,当 ionic 有 manjor 更新时,不太可能破坏。
https://gist.github.com/haydenbr/7df417a8678efc404c820c61b6ffdd24
所以缓存破坏与离子。这是一个骇人听闻的解决方案,但目前有效。问题是离子构建系统有时抽象得太多了。早在 10 月份,就有人询问是否有一种方法可以实现缓存清除。 ionic 团队回应说他们可能会在未来考虑它,但是从那以后就没有任何 activity 了。 Here's the github issue.
因此,我将展示对 webpack 配置和 package.json 的所有更改,然后解释所有内容。
您的 package.json 的配置部分应该如下所示。
"config": {
"ionic_webpack": "./webpack.config.js",
"ionic_source_map_type": "source-map",
"ionic_uglifyjs": "./www/uglifyjs.config.json"
}
对于您的 webpack 配置,您的输入和输出可以保持不变。确保您需要以下模块,然后您需要添加以下插件:
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,设置标题,为 javascript 输出注入一个 <script>
标记,然后将其写入您在文件名中指定的位置 属性。如果您使用的是来自 ionic starter 应用程序的默认 index.html,那么您需要做的就是摆脱 <script src="build/main.js"></script>
,它会自动将新的 link 放入他们用于其中包含哈希的文件名。 Docs here.
下一个插件会为我们输出一个清单文件,这样我们就可以知道文件名和哈希值是什么。默认情况下,它在 www/build/
中输出。 Docs here.
下一个插件是将新文件名分配给 process.env.IONIC_OUTPUT_JS_FILE_NAME 并为我们生成新的 uglify 配置。我们几乎获取了输出的清单,将新的 uglify 配置写入 www 目录,然后根据我们从清单中获得的内容分配新文件名。
差不多就是这样。如果您不想为开发人员破坏缓存,请保留 html 插件,去掉其他两个,然后将输出文件名改回 process.env.IONIC_OUTPUT_JS_FILE_NAME
。如果这样做,您根本不需要在 src/index.html
中引用主 js 文件。无论您的 运行 开发还是生产,它都会被放入。有关针对不同环境使用不同 webpack 设置的更多信息,check this out.
更新:
对于离子 3:
- 确保您在
tsconfig
的compilerOptions
中有这些设置:
"module": "es2015", "target": "es5"
npm i cheerio --save-dev
- 在你的 webpack 配置中添加
var cheerio = require('cheerio')
- 去掉 Webpack Manifest 插件。
将
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); }); }
去掉 Html Webpack 插件
代替 html 插件,将以下函数放在 webpack 配置的插件数组中:
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() ); }); }
我从一个 ionic 论坛线程(https://forum.ionicframework.com/t/file-revisions/75028/2,作者 aszmyd)找到了解决此问题的更好方法,该线程也解决了对 main.css 文件名进行哈希处理的问题。我对脚本做了一些小的调整,因为我没有 oauth.html 或 kajam.js。
此解决方案的优势在于它不会尝试拦截离子构建,而只会处理结果。
(毫无疑问,有人会挑剔我 post 的方法,但这对我很有用,我希望对其他人也有用。我无法想象没有完整缓存的网络应用程序-破坏所有 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');
}
此 solution 与 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());