liferay-theme-tasks
Version:
A set of tasks for building and deploying Liferay Portal themes.
368 lines (319 loc) • 8.42 kB
JavaScript
/**
* SPDX-FileCopyrightText: © 2017 Liferay, Inc. <https://liferay.com>
* SPDX-License-Identifier: MIT
*/
;
const chalk = require('chalk');
const fs = require('fs-extra');
const gulpIf = require('gulp-if');
const rename = require('gulp-rename');
const replace = require('gulp-replace-task');
const sourcemaps = require('gulp-sourcemaps');
const terser = require('gulp-terser');
const _ = require('lodash');
const path = require('path');
const PluginError = require('plugin-error');
const through = require('through2');
const project = require('../../../lib/project');
const gulpR2 = require('../../../lib/r2');
const themeUtil = require('../../../lib/util');
const getBaseThemeDependencies = require('../../lib/getBaseThemeDependencies');
const lookAndFeelUtil = require('../../lib/look_and_feel_util');
const normalize = require('../../lib/normalize');
/**
* Add JS-injection placeholders (HTML comments of the form
* `<!-- inject:js -->`/`<!-- endinject -->`) to templates
* in the base theme dependencies.
*/
function injectJS() {
const targetRegExp = new RegExp(
'/liferay-frontend-theme-unstyled/templates/portal_normal\\.(ftl|vm)$'
);
return through.obj(function (file, encoding, callback) {
if (!file.path.match(targetRegExp) || file.isNull()) {
// Nothing to do.
}
else if (file.isStream()) {
file.contents = file.contents.pipe(() => {
let output = '';
return through(
function transform(chunk, encoding, transformCallback) {
output += chunk.toString();
transformCallback(null);
},
function flush(flushCallback) {
this.push(normalize(output));
flushCallback(null);
}
);
});
}
else if (file.isBuffer()) {
file.contents = Buffer.from(
normalize(file.contents.toString('utf8'))
);
}
else {
return this.emit(
'error',
new PluginError('injectJS', 'Unsupported file type')
);
}
return callback(null, file);
});
}
module.exports = function () {
const {gulp, options} = project;
const {runSequence} = gulp;
const {pathBuild, pathSrc} = options;
gulp.task('build', (callback) => {
runSequence(
'build:clean',
'build:base',
'build:src',
'build:web-inf',
'build:liferay-look-and-feel',
'build:hook',
'build:themelets',
'build:rename-css-dir',
'build:compile-css',
'build:fix-url-functions',
'build:move-compiled-css',
'build:remove-old-css-dir',
'build:fix-at-directives',
'build:r2',
'build:copy:fontAwesome',
'build:minify:js',
'build:war',
callback
);
});
gulp.task('build:clean', (callback) => {
fs.removeSync(pathBuild.resolve().asNative);
callback();
});
gulp.task('build:base', () => {
const sourceFiles = getBaseThemeDependencies();
return gulp
.src(sourceFiles)
.pipe(injectJS())
.pipe(gulp.dest(pathBuild.asNative));
});
gulp.task('build:src', () => {
return gulp
.src(pathSrc.join('**', '*').asPosix, {
base: pathSrc.asNative,
})
.pipe(gulp.dest(pathBuild.asNative));
});
gulp.task('build:web-inf', () => {
return gulp
.src(pathBuild.join('WEB-INF', 'src', '**', '*').asPosix, {
base: pathBuild.join('WEB-INF', 'src').asNative,
})
.pipe(gulp.dest(pathBuild.join('WEB-INF', 'classes').asNative));
});
gulp.task('build:liferay-look-and-feel', (callback) => {
const themePath = project.dir;
lookAndFeelUtil.mergeLookAndFeelJSON(
themePath,
{},
(lookAndFeelJSON) => {
if (!lookAndFeelJSON) {
return callback();
}
const themeName = lookAndFeelUtil.getNameFromPluginPackageProperties(
themePath
);
lookAndFeelUtil.correctJSONIdentifiers(
lookAndFeelJSON,
themeName
);
let doctypeElement = lookAndFeelUtil.getLookAndFeelDoctype(
themePath
);
if (!doctypeElement) {
const themeConfig = project.themeConfig.config;
doctypeElement = lookAndFeelUtil.getLookAndFeelDoctypeByVersion(
themeConfig.version
);
}
const xml = lookAndFeelUtil.buildXML(
lookAndFeelJSON,
doctypeElement
);
fs.writeFile(
path.join(
themePath,
pathBuild.asNative,
'WEB-INF',
'liferay-look-and-feel.xml'
),
xml,
(error) => {
if (error) {
throw error;
}
callback();
}
);
}
);
});
gulp.task('build:hook', () => {
const languageProperties = themeUtil.getLanguageProperties(
pathSrc.asNative
);
return gulp
.src(pathBuild.join('WEB-INF', 'liferay-hook.xml').asPosix, {
allowEmpty: true,
})
.pipe(
replace({
patterns: [
{
match: /<language-properties>content\/Language\*\.properties<\/language-properties>/,
replacement() {
let retVal = '';
if (languageProperties) {
retVal = languageProperties.join('\n\t');
}
return retVal;
},
},
],
})
)
.pipe(rename('liferay-hook.xml'))
.pipe(gulp.dest(pathBuild.join('WEB-INF').asNative));
});
gulp.task('build:rename-css-dir', (callback) => {
fs.rename(
pathBuild.join('css').asNative,
pathBuild.join('_css').asNative,
callback
);
});
// Temp fix for libSass compilation issue with empty url() functions
gulp.task('build:fix-url-functions', (callback) => {
gulp.src(pathBuild.join('_css', '**', '*.css').asPosix)
.pipe(
replace({
patterns: [
{
match: /url\(url\(\)/g,
replacement: 'url()',
},
],
})
)
.pipe(
gulp.dest(pathBuild.join('_css').asNative, {
overwrite: true,
})
)
.on('end', callback);
});
gulp.task('build:move-compiled-css', () => {
return gulp
.src(pathBuild.join('_css', '**', '*').asPosix)
.pipe(gulp.dest(pathBuild.join('css').asNative));
});
gulp.task('build:remove-old-css-dir', (callback) => {
fs.removeSync(pathBuild.join('_css').asNative);
callback();
});
gulp.task('build:fix-at-directives', () => {
return gulp
.src(pathBuild.join('css', '*.css').asPosix)
.pipe(
replace({
patterns: getFixAtDirectivesPatterns(),
})
)
.pipe(gulp.dest(pathBuild.join('css').asNative));
});
gulp.task('build:r2', () => {
const r2 = gulpR2();
return gulp
.src(pathBuild.join('css', '*.css').asPosix)
.pipe(
rename({
suffix: '_rtl',
})
)
.pipe(r2)
.pipe(gulp.dest(pathBuild.join('css').asNative))
.on('end', () => {
if (r2.hasCssParseErrors) {
// eslint-disable-next-line no-console
console.log(
chalk.yellow.bold(
'\n' +
'Some CSS files had incorrect SASS or CSS code. The build will finish but\n' +
'the RTL CSS will not be generated correctly.\n' +
'\n' +
'Please address the issues in your source code to see this error go away.\n'
)
);
}
});
});
gulp.task('build:copy:fontAwesome', (done) => {
const packageJSON = JSON.parse(fs.readFileSync('package.json', 'utf8'))
.liferayTheme;
if (!packageJSON.fontAwesome) {
return done();
}
const liferayFontAwesome = themeUtil.resolveDependency(
'liferay-font-awesome'
);
fs.copy(
path.join(liferayFontAwesome, 'font'),
pathBuild.join('font').asNative,
done
);
});
gulp.task('build:minify:js', () => {
const production = process.env.NODE_ENV === 'production';
return gulp
.src(pathBuild.join('**', '*.js').asPosix)
.pipe(gulpIf(production, sourcemaps.init()))
.pipe(gulpIf(production, terser(options.terser || {})))
.pipe(gulpIf(production, sourcemaps.write('.')))
.pipe(gulp.dest(pathBuild));
});
gulp.task('build:war', (done) => {
runSequence('plugin:version', 'plugin:war', done);
});
};
function getFixAtDirectivesPatterns() {
const keyframeRulesReplace = function (match, m1, m2) {
return (
_.map(m1.split(','), (item) => {
return item.replace(/.*?(from|to|[0-9.]+%)/g, '$1');
}).join(',') + m2
);
};
return [
{
match: /(@font-face|@page|@-ms-viewport)\s*({\n\s*)(.*)\s*({)/g,
replacement(match, m1, m2, m3, m4) {
return m3 + m2 + m1 + ' ' + m4;
},
},
{
match: /(@-ms-keyframes.*{)([\s\S]+?)(}\s})/g,
replacement(match, m1, m2, m3) {
m2 = m2.replace(/(.+?)(\s?{)/g, keyframeRulesReplace);
return m1 + m2 + m3;
},
},
{
match: /@import\s+url\s*\(\s*['"]?(.+\.css)['"]?/g,
replacement(match, m1) {
return '@import url(' + m1 + '?t=' + Date.now();
},
},
];
}