mya-jinja
Version:
Support Jinja2 end template engine in mya
214 lines (183 loc) • 6.51 kB
JavaScript
/**
* mya jinja 解决方案
* @dependance: mya-server-jina fis-optimizer-jinja-xss
*/
const path = require('path');
const CLIEngine = require("eslint").CLIEngine;
// 补全模版路径
function getFullTplPath(content, templatePublishPath) {
// {% extends "path" %} -> {% extends "${templatePublishPath/path" %}
content = content.replace(/({%\s*(?:extends|include)\s+["|'])(\S+)(["|'](?:.+?)%})/ig, function(match, p1, p2, p3) {
if (p2.charAt(0) === '/') {
return `${p1}${templatePublishPath}${p2}${p3}`;
} else {
return `${p1}${templatePublishPath}/${p2}${p3}`;
}
});
return content;
}
function tplPathProcessor(content, file, conf) {
const templatePublishPath = fis.get('build.templatePublishPath');
return getFullTplPath(content, templatePublishPath);
}
// 支持 {% uri src="static/image/i18n/{{ _mya_locale }}/xxx.png" %} 动态路径,自动补充 namespace
function dynamicPathFix(content, file, conf) {
const ns = fis.get('namespace');
content = content.replace(/({%\s*uri\s+src\s*=\s*['"])(.+?)(['"]\s*%})/ig, function(match, left, uri, right) {
if (uri.indexOf(ns + ':') === 0) {
return `${left}${uri}${right}`;
} else {
return `${left}${ns}:${uri}${right}`;
}
});
return content;
}
function retMapPostpackager(ret) {
const root = fis.project.getProjectPath();
const map = fis.file.wrap(path.join(root, fis.get('namespace') ? fis.get('namespace') + '-map.json' : 'map.json'));;
map.setContent(JSON.stringify(ret.map, null, map.optimizer ? null : 4)); // ret.map 为静态资源映射表
ret.pkg[map.subpath] = map;
}
function lintES6(content, file, conf) {
const options = {
env: [ 'browser' ],
useEslintrc: false,
plugins: [
'es'
],
parserOptions: {
ecmaVersion: 6
},
baseConfig: {
extends: [
'plugin:es/no-2015'
],
},
rules: {
'es/no-rest-spread-properties': 'off',
'es/no-template-literals': 'off',
'es/no-block-scoped-functions': 'off'
}
};
const CLIEngine = require("eslint").CLIEngine;
const cli = new CLIEngine(options);
const jinjaReservedPattern = /\{(\{|#|%)(.*?)(\}|\1)\}/ig;
let temp = content.replace(jinjaReservedPattern, function(match) {
return 1 + new Array(match.length).join(0);
});
const messages = cli.executeOnText(temp, file.basename);
const errorReport = CLIEngine.getErrorResults(messages.results);
if (errorReport && Array.isArray(errorReport) && errorReport.length) {
errorReport[0].messages.forEach(messageObj => {
process.stdout.write('\n ' + `${file.fullname}`.underline + '\n ' + 'ERROR'.red + ' ' + `${messageObj.message}`.yellow + ' ' + `${messageObj.ruleId}` + '\n');
})
process.exit(1);
}
return content;
}
module.exports = function(fis, opt) {
fis.set('server.type', 'jinja');
fis.set('settings.parser.babel-7.x', {
noSourceMaps: true
});
fis.match('*.html', { // 仅 html 文件开启同名依赖,依赖同名 css/js
useSameNameRequire: true,
useCache: false
});
fis.match('/(**.html)', {
useMap: true,
preprocessor: fis.plugin('extlang'),
postprocessor: [
tplPathProcessor,
dynamicPathFix,
],
optimizer: [
fis.plugin('jinja-xss'),
fis.plugin('html-minifier', {
// 不处理自定义注释
ignoreCustomComments: [/^\s*(STYLE_PLACEHOLDER|SCRIPT_PLACEHOLDER|I18N_MAP_PLACEHOLDER)/],
// 不处理 jinja2 语法
ignoreCustomFragments: [
/{#[\s\S]*?#}/,
/{{[\s\S]*?}}/,
/{%\s*script[\s\S]*?endscript\s*%}/,
/{%[\s\S]*?%}/,
],
})
]
}, true);
fis.match('${namespace}-map.json', {
release: '/template/mya_conf/$0'
});
fis.match('::package', {
postpackager: function createMap(ret) {
retMapPostpackager(ret);
}
});
// deploy 不走 skip-packed 插件
fis.match('**', {
deploy: fis.plugin('local-deliver')
});
fis.match('/{component,page}/**/mock.json', {
useCompile: false,
rExt: 'json',
release: false
});
// 模板文件中的js不要使用 es6,因为模板文件可以传递变量,比如 init({{ rate|e }}),这种在编译时会报错
fis.match('/{component,page,common,*_common}/**.html:js', {
lint: null,
preprocessor: null,
rExt: 'js',
parser: lintES6,
optimizer: null
});
// 开发配置
const devRules = {
'/test/api/(**.{json,js})': {
useHash: false,
isMod: false,
useCompile: false,
release: 'mock/${namespace}/api/$1'
},
// 全局 mock 数据
'/test/page/mock.json': {
useHash: false,
release: 'mock/${namespace}/page/mock.json'
},
// 就近维护mock数据(推荐)
'/page/(**)/mock.json': {
userHash: false,
release: 'mock/${namespace}/page/$1/index.json'
},
'/component/(**)/mock.json': {
userHash: false,
release: 'mock/${namespace}/component/$1/index.json'
},
'::package': {
postpackager: function createMap(ret) {
retMapPostpackager(ret);
}
},
'server.{page,mock,proxy}.conf': {
preprocessor: function(content, file, conf) {
const namespace = fis.get('namespace');
return content.replace(/(rewrite\s+\S+\s+)(\S+)/g, `$1/${namespace}$2`)
},
release: '$0'
},
'server.conf': {
release: 'server-conf/${namespace}.conf'
}
};
['develop', 'mock', 'prod'].forEach(function(media) {
Object.keys(devRules).forEach(function(key) {
fis.media(media).match(key, devRules[key], true); // 防止规则被覆盖
});
});
if (opt && opt.i18n) {
require('./plugin/i18n.js')(fis, opt, retMapPostpackager);
}
if (opt && opt.falcon) {
require('./plugin/falcon.js')(fis, opt);
}
};