feat(build): scan emails for invalid style attributes
adds a grunt task to generate the emails and check the style attributes
This commit is contained in:
Родитель
8c62233812
Коммит
b14b59f00b
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
.tmp
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
module.exports = function (grunt) {
|
||||
// load all grunt tasks based on environment
|
||||
// show elapsed time at the end
|
||||
require('time-grunt')(grunt);
|
||||
require('load-grunt-tasks')(grunt);
|
||||
|
||||
grunt.initConfig({
|
||||
pkg: grunt.file.readJSON('package.json')
|
||||
});
|
||||
|
||||
grunt.loadTasks('grunttasks');
|
||||
};
|
|
@ -0,0 +1,74 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// grunt task to create .json files out of .po files.
|
||||
// .po files are expected to already be downloaded.
|
||||
|
||||
var path = require('path');
|
||||
var i18n = require('i18n-abide');
|
||||
|
||||
module.exports = function (grunt) {
|
||||
grunt.config('po2json', {
|
||||
options: {
|
||||
format: 'raw',
|
||||
fuzzy: true,
|
||||
output_filename: function (file) { //eslint-disable-line camelcase
|
||||
|
||||
/**
|
||||
* the files are stored in the locale subdirectory with a directory
|
||||
* structure of:
|
||||
* locale/
|
||||
* <locale_name>/
|
||||
* LC_MESSAGES/
|
||||
* server.po
|
||||
* client.po
|
||||
*
|
||||
* Each locale is stored in its own subdirectory in the output i18n
|
||||
* directory.
|
||||
**/
|
||||
var matches = /^locale\/([^\/]+)\/LC_MESSAGES\/(.*)$/.exec(file);
|
||||
|
||||
// In order to make sure the locale is in a form that i18n-abide expects
|
||||
// we convert it from locale to langauge back to locale.
|
||||
var locale = i18n.normalizeLocale(matches[1]);
|
||||
var filename = matches[2];
|
||||
if (filename === 'server.po') {
|
||||
filename = 'messages.json';
|
||||
} else {
|
||||
// get rid of the .po extension, replace with .json
|
||||
filename = path.basename(filename, '.po') + '.json';
|
||||
}
|
||||
return locale + '/' + filename;
|
||||
},
|
||||
output_transform: function (data) { //eslint-disable-line camelcase
|
||||
// write the first translation only, ignore pluralization.
|
||||
var isArray = function (item) {
|
||||
return Object.prototype.toString.call(item) === '[object Array]';
|
||||
};
|
||||
|
||||
var transformed = {};
|
||||
for (var msgid in data) {
|
||||
var translation = data[msgid];
|
||||
if (isArray(translation) && translation.length >= 2) {
|
||||
translation = translation[1];
|
||||
}
|
||||
|
||||
transformed[msgid] = translation;
|
||||
}
|
||||
|
||||
return transformed;
|
||||
}
|
||||
},
|
||||
all: {
|
||||
src: ['locale/**/**/*.po'],
|
||||
dest: '.tmp/l10n/'
|
||||
},
|
||||
template: {
|
||||
src: ['locale/templates/**/*.pot'],
|
||||
dest: '.tmp/l10n'
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,115 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
module.exports = function (grunt) {
|
||||
grunt.registerTask('scan-email-translations', 'Scan the generated emails for invalid style tags', [
|
||||
'generate-valid-styles',
|
||||
'po2json',
|
||||
'verify-styles'
|
||||
]
|
||||
);
|
||||
|
||||
grunt.registerMultiTask('verify-styles', 'Verify whether the translated styles are valid', function (){
|
||||
var options = this.options({
|
||||
validStyles: []
|
||||
});
|
||||
var styleArray = options.validStyles;
|
||||
|
||||
var fileArray = this.files[0].src;
|
||||
|
||||
var hasErrors = 0;
|
||||
var styleRegex = /style="[\S\s]*?"/ig;
|
||||
|
||||
fileArray.forEach(function (src) {
|
||||
var json = grunt.file.readJSON(src);
|
||||
var styleList = [];
|
||||
for(var key in json) {
|
||||
if(key.indexOf('style') > 0) {
|
||||
styleList.push(key);
|
||||
}
|
||||
}
|
||||
// Check if there were any styles found, .match() might return null
|
||||
if (styleList !== null) {
|
||||
styleList = styleList.map(stripSpecialChars);
|
||||
// retrieve the style attribute and its contents and store
|
||||
styleList = styleList.map(function (str) {
|
||||
if (styleRegex.test(str)) {
|
||||
str = str.match(styleRegex)[0];
|
||||
return str;
|
||||
}
|
||||
});
|
||||
styleList.forEach(function (style) {
|
||||
if(style !== undefined){
|
||||
if (styleArray.indexOf(style) === -1) {
|
||||
grunt.log.error('Found a style which should not be there', style, src);
|
||||
hasErrors = hasErrors + 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
if (hasErrors !== 0) {
|
||||
grunt.fail.warn('Found ' + hasErrors + ' ' + grunt.util.pluralize(hasErrors, 'error/errors') + ' in ' + fileArray.length + ' files');
|
||||
} else {
|
||||
grunt.log.writeln('Checked ' + fileArray.length + ' files for valid styles and found nothing wrong.');
|
||||
}
|
||||
});
|
||||
|
||||
grunt.config('verify-styles', {
|
||||
dist: {
|
||||
options: {
|
||||
validStyles: grunt.file.readJSON('.tmp/validStyles.json')
|
||||
},
|
||||
files: [
|
||||
{
|
||||
expand: false,
|
||||
src: [
|
||||
'.tmp/l10n/*/*.json'
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
grunt.registerMultiTask('generate-valid-styles', 'Generate the list of valid styles for translation', function () {
|
||||
//different regex from above, uses single quotes not double
|
||||
var styleRegex = /style='[\s\S]*?'/ig;
|
||||
var styleArray = [];
|
||||
|
||||
// iterate through .pot file and extract the styles
|
||||
this.files[0].src.forEach(function (src) {
|
||||
var contents = grunt.file.read(src);
|
||||
if (styleRegex.test(contents)) {
|
||||
Array.prototype.push.apply(styleArray, contents.match(styleRegex));
|
||||
}
|
||||
});
|
||||
styleArray = styleArray.map(stripSpecialChars);
|
||||
// write out the styles to a temporary JSON file
|
||||
grunt.file.write(this.files[0].dest, JSON.stringify(styleArray));
|
||||
grunt.log.writeln('Generated styles from ' + this.files[0].src.length + ' files');
|
||||
});
|
||||
|
||||
grunt.config('generate-valid-styles', {
|
||||
dist: {
|
||||
files: [
|
||||
{
|
||||
expand: false,
|
||||
src: [
|
||||
'locale/templates/*/server.pot'
|
||||
],
|
||||
dest: '.tmp/validStyles.json'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
function stripSpecialChars(str) {
|
||||
str = str.replace(/'/g, '"');
|
||||
str = str.replace(/""/g, '');
|
||||
str = str.replace(/\n/g, '');
|
||||
return str.replace(/(\\)/g, '');
|
||||
}
|
||||
|
||||
};
|
|
@ -13,5 +13,12 @@
|
|||
"license": "MPL-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/mozilla/fxa-content-server-l10n/issues"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "0.4.5",
|
||||
"grunt-po2json": "git://github.com/shane-tomlinson/grunt-po2json.git#aa276503e",
|
||||
"i18n-abide": "0.0.23",
|
||||
"load-grunt-tasks": "3.2.0",
|
||||
"time-grunt": "1.2.1"
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче