MOBILE-3501 gulp: Split gulpfile code into several classes
parent
b67ea14abb
commit
0d6d4a9326
|
@ -0,0 +1,113 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const through = require('through');
|
||||||
|
const bufferFrom = require('buffer-from');
|
||||||
|
const rename = require('gulp-rename');
|
||||||
|
const exec = require('child_process').exec;
|
||||||
|
|
||||||
|
const LICENSE = '' +
|
||||||
|
'// (C) Copyright 2015 Moodle Pty Ltd.\n' +
|
||||||
|
'//\n' +
|
||||||
|
'// Licensed under the Apache License, Version 2.0 (the "License");\n' +
|
||||||
|
'// you may not use this file except in compliance with the License.\n' +
|
||||||
|
'// You may obtain a copy of the License at\n' +
|
||||||
|
'//\n' +
|
||||||
|
'// http://www.apache.org/licenses/LICENSE-2.0\n' +
|
||||||
|
'//\n' +
|
||||||
|
'// Unless required by applicable law or agreed to in writing, software\n' +
|
||||||
|
'// distributed under the License is distributed on an "AS IS" BASIS,\n' +
|
||||||
|
'// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' +
|
||||||
|
'// See the License for the specific language governing permissions and\n' +
|
||||||
|
'// limitations under the License.\n\n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task to convert config.json into a TypeScript class.
|
||||||
|
*/
|
||||||
|
class BuildConfigTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the task.
|
||||||
|
*
|
||||||
|
* @param path Path to the config file.
|
||||||
|
* @param done Function to call when done.
|
||||||
|
*/
|
||||||
|
run(path, done) {
|
||||||
|
// Get the last commit.
|
||||||
|
exec('git log -1 --pretty=format:"%H"', (err, commit, stderr) => {
|
||||||
|
if (err) {
|
||||||
|
console.error('An error occurred while getting the last commit: ' + err);
|
||||||
|
} else if (stderr) {
|
||||||
|
console.error('An error occurred while getting the last commit: ' + stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
gulp.src(path)
|
||||||
|
.pipe(through(function(file) {
|
||||||
|
// Convert the contents of the file into a TypeScript class.
|
||||||
|
// Disable the rule variable-name in the file.
|
||||||
|
const config = JSON.parse(file.contents.toString());
|
||||||
|
let contents = LICENSE + '// tslint:disable: variable-name\n' + 'export class CoreConfigConstants {\n';
|
||||||
|
|
||||||
|
for (let key in config) {
|
||||||
|
let value = config[key];
|
||||||
|
|
||||||
|
if (typeof value == 'string') {
|
||||||
|
// Wrap the string in ' and escape them.
|
||||||
|
value = "'" + value.replace(/([^\\])'/g, "$1\\'") + "'";
|
||||||
|
} else if (typeof value != 'number' && typeof value != 'boolean') {
|
||||||
|
// Stringify with 4 spaces of indentation, and then add 4 more spaces in each line.
|
||||||
|
value = JSON.stringify(value, null, 4).replace(/^(?: )/gm, ' ').replace(/^(?:})/gm, ' }');
|
||||||
|
// Replace " by ' in values.
|
||||||
|
value = value.replace(/: "([^"]*)"/g, ": '$1'");
|
||||||
|
|
||||||
|
// Check if the keys have "-" in it.
|
||||||
|
const matches = value.match(/"([^"]*\-[^"]*)":/g);
|
||||||
|
if (matches) {
|
||||||
|
// Replace " by ' in keys. We cannot remove them because keys have chars like '-'.
|
||||||
|
value = value.replace(/"([^"]*)":/g, "'$1':");
|
||||||
|
} else {
|
||||||
|
// Remove ' in keys.
|
||||||
|
value = value.replace(/"([^"]*)":/g, "$1:");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add type any to the key.
|
||||||
|
key = key + ': any';
|
||||||
|
}
|
||||||
|
|
||||||
|
// If key has quotation marks, remove them.
|
||||||
|
if (key[0] == '"') {
|
||||||
|
key = key.substr(1, key.length - 2);
|
||||||
|
}
|
||||||
|
contents += ' static ' + key + ' = ' + value + ';\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add compilation info.
|
||||||
|
contents += ' static compilationtime = ' + Date.now() + ';\n';
|
||||||
|
contents += ' static lastcommit = \'' + commit + '\';\n';
|
||||||
|
|
||||||
|
contents += '}\n';
|
||||||
|
|
||||||
|
file.contents = bufferFrom(contents);
|
||||||
|
|
||||||
|
this.emit('data', file);
|
||||||
|
}))
|
||||||
|
.pipe(rename('configconstants.ts'))
|
||||||
|
.pipe(gulp.dest('./src'))
|
||||||
|
.on('end', done);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BuildConfigTask;
|
|
@ -0,0 +1,176 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const slash = require('gulp-slash');
|
||||||
|
const clipEmptyFiles = require('gulp-clip-empty-files');
|
||||||
|
const through = require('through');
|
||||||
|
const bufferFrom = require('buffer-from');
|
||||||
|
const File = require('vinyl');
|
||||||
|
const pathLib = require('path');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task to build the language files into a single file per language.
|
||||||
|
*/
|
||||||
|
class BuildLangTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy a property from one object to another, adding a prefix to the key if needed.
|
||||||
|
*
|
||||||
|
* @param target Object to copy the properties to.
|
||||||
|
* @param source Object to copy the properties from.
|
||||||
|
* @param prefix Prefix to add to the keys.
|
||||||
|
*/
|
||||||
|
addProperties(target, source, prefix) {
|
||||||
|
for (let property in source) {
|
||||||
|
target[prefix + property] = source[property];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the task.
|
||||||
|
*
|
||||||
|
* @param language Language to treat.
|
||||||
|
* @param langPaths Paths to the possible language files.
|
||||||
|
* @param done Function to call when done.
|
||||||
|
*/
|
||||||
|
run(language, langPaths, done) {
|
||||||
|
const filename = language + '.json';
|
||||||
|
const data = {};
|
||||||
|
let firstFile = null;
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
const paths = langPaths.map((path) => {
|
||||||
|
if (path.slice(-1) != '/') {
|
||||||
|
path = path + '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
return path + language + '.json';
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.src(paths, { allowEmpty: true })
|
||||||
|
.pipe(slash())
|
||||||
|
.pipe(clipEmptyFiles())
|
||||||
|
.pipe(through(function(file) {
|
||||||
|
if (!firstFile) {
|
||||||
|
firstFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self.treatFile(file, data);
|
||||||
|
}, function() {
|
||||||
|
/* This implementation is based on gulp-jsoncombine module.
|
||||||
|
* https://github.com/reflog/gulp-jsoncombine */
|
||||||
|
if (firstFile) {
|
||||||
|
const joinedPath = pathLib.join(firstFile.base, language + '.json');
|
||||||
|
|
||||||
|
const joinedFile = new File({
|
||||||
|
cwd: firstFile.cwd,
|
||||||
|
base: firstFile.base,
|
||||||
|
path: joinedPath,
|
||||||
|
contents: self.treatMergedData(data),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.emit('data', joinedFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.emit('end');
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest(pathLib.join('./src/assets', 'lang')))
|
||||||
|
.on('end', done);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Treats a file to merge JSONs. This function is based on gulp-jsoncombine module.
|
||||||
|
* https://github.com/reflog/gulp-jsoncombine
|
||||||
|
*
|
||||||
|
* @param file File treated.
|
||||||
|
* @param data Object where to store the data.
|
||||||
|
*/
|
||||||
|
treatFile(file, data) {
|
||||||
|
if (file.isNull() || file.isStream()) {
|
||||||
|
return; // ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
let srcPos = file.path.lastIndexOf('/src/');
|
||||||
|
if (srcPos == -1) {
|
||||||
|
// It's probably a Windows environment.
|
||||||
|
srcPos = file.path.lastIndexOf('\\src\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
const path = file.path.substr(srcPos + 5);
|
||||||
|
data[path] = JSON.parse(file.contents.toString());
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Error parsing JSON: ' + err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Treats the merged JSON data, adding prefixes depending on the component.
|
||||||
|
*
|
||||||
|
* @param data Merged data.
|
||||||
|
* @return Buffer with the treated data.
|
||||||
|
*/
|
||||||
|
treatMergedData(data) {
|
||||||
|
const merged = {};
|
||||||
|
const mergedOrdered = {};
|
||||||
|
|
||||||
|
for (let filepath in data) {
|
||||||
|
const pathSplit = filepath.split(/[\/\\]/);
|
||||||
|
let prefix;
|
||||||
|
|
||||||
|
pathSplit.pop();
|
||||||
|
|
||||||
|
switch (pathSplit[0]) {
|
||||||
|
case 'lang':
|
||||||
|
prefix = 'core';
|
||||||
|
break;
|
||||||
|
case 'core':
|
||||||
|
if (pathSplit[1] == 'lang') {
|
||||||
|
// Not used right now.
|
||||||
|
prefix = 'core';
|
||||||
|
} else {
|
||||||
|
prefix = 'core.' + pathSplit[1];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'addon':
|
||||||
|
// Remove final item 'lang'.
|
||||||
|
pathSplit.pop();
|
||||||
|
// Remove first item 'addon'.
|
||||||
|
pathSplit.shift();
|
||||||
|
|
||||||
|
// For subplugins. We'll use plugin_subfolder_subfolder2_...
|
||||||
|
// E.g. 'mod_assign_feedback_comments'.
|
||||||
|
prefix = 'addon.' + pathSplit.join('_');
|
||||||
|
break;
|
||||||
|
case 'assets':
|
||||||
|
prefix = 'assets.' + pathSplit[1];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prefix) {
|
||||||
|
this.addProperties(merged, data[filepath], prefix + '.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force ordering by string key.
|
||||||
|
Object.keys(merged).sort().forEach((key) => {
|
||||||
|
mergedOrdered[key] = merged[key];
|
||||||
|
});
|
||||||
|
|
||||||
|
return bufferFrom(JSON.stringify(mergedOrdered, null, 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = BuildLangTask;
|
|
@ -0,0 +1,164 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const through = require('through');
|
||||||
|
const bufferFrom = require('buffer-from');
|
||||||
|
const concat = require('gulp-concat');
|
||||||
|
const pathLib = require('path');
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task to combine scss into a single file.
|
||||||
|
*/
|
||||||
|
class CombineScssTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds the file and returns its content.
|
||||||
|
*
|
||||||
|
* @param capture Import file path.
|
||||||
|
* @param baseDir Directory where the file was found.
|
||||||
|
* @param paths Alternative paths where to find the imports.
|
||||||
|
* @param parsedFiles Already parsed files to reduce size of the result.
|
||||||
|
* @return Partially combined scss.
|
||||||
|
*/
|
||||||
|
getReplace(capture, baseDir, paths, parsedFiles) {
|
||||||
|
let parse = pathLib.parse(pathLib.resolve(baseDir, capture + '.scss'));
|
||||||
|
let file = parse.dir + '/' + parse.name;
|
||||||
|
|
||||||
|
if (file.slice(-3) === '.wp') {
|
||||||
|
console.log('Windows Phone not supported "' + capture);
|
||||||
|
// File was already parsed, leave the import commented.
|
||||||
|
return '// @import "' + capture + '";';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!fs.existsSync(file + '.scss')) {
|
||||||
|
// File not found, might be a partial file.
|
||||||
|
file = parse.dir + '/_' + parse.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If file still not found, try to find the file in the alternative paths.
|
||||||
|
let x = 0;
|
||||||
|
while (!fs.existsSync(file + '.scss') && paths.length > x) {
|
||||||
|
parse = pathLib.parse(pathLib.resolve(paths[x], capture + '.scss'));
|
||||||
|
file = parse.dir + '/' + parse.name;
|
||||||
|
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
|
||||||
|
file = file + '.scss';
|
||||||
|
|
||||||
|
if (!fs.existsSync(file)) {
|
||||||
|
// File not found. Leave the import there.
|
||||||
|
console.log('File "' + capture + '" not found');
|
||||||
|
return '@import "' + capture + '";';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsedFiles.indexOf(file) >= 0) {
|
||||||
|
console.log('File "' + capture + '" already parsed');
|
||||||
|
// File was already parsed, leave the import commented.
|
||||||
|
return '// @import "' + capture + '";';
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedFiles.push(file);
|
||||||
|
const text = fs.readFileSync(file);
|
||||||
|
|
||||||
|
// Recursive call.
|
||||||
|
return this.scssCombine(text, parse.dir, paths, parsedFiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the task.
|
||||||
|
*
|
||||||
|
* @param done Function to call when done.
|
||||||
|
*/
|
||||||
|
run(done) {
|
||||||
|
const paths = [
|
||||||
|
'node_modules/ionic-angular/themes/',
|
||||||
|
'node_modules/font-awesome/scss/',
|
||||||
|
'node_modules/ionicons/dist/scss/'
|
||||||
|
];
|
||||||
|
const parsedFiles = [];
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
gulp.src([
|
||||||
|
'./src/theme/variables.scss',
|
||||||
|
'./node_modules/ionic-angular/themes/ionic.globals.*.scss',
|
||||||
|
'./node_modules/ionic-angular/themes/ionic.components.scss',
|
||||||
|
'./src/**/*.scss',
|
||||||
|
]).pipe(through(function(file) { // Combine them based on @import and save it to stream.
|
||||||
|
if (file.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedFiles.push(file);
|
||||||
|
file.contents = bufferFrom(self.scssCombine(
|
||||||
|
file.contents, pathLib.dirname(file.path), paths, parsedFiles));
|
||||||
|
|
||||||
|
this.emit('data', file);
|
||||||
|
})).pipe(concat('combined.scss')) // Concat the stream output in single file.
|
||||||
|
.pipe(gulp.dest('.')) // Save file to destination.
|
||||||
|
.on('end', done);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combine scss files with its imports
|
||||||
|
*
|
||||||
|
* @param content Scss string to treat.
|
||||||
|
* @param baseDir Directory where the file was found.
|
||||||
|
* @param paths Alternative paths where to find the imports.
|
||||||
|
* @param parsedFiles Already parsed files to reduce size of the result.
|
||||||
|
* @return Scss string with the replaces done.
|
||||||
|
*/
|
||||||
|
scssCombine(content, baseDir, paths, parsedFiles) {
|
||||||
|
// Content is a Buffer, convert to string.
|
||||||
|
if (typeof content != "string") {
|
||||||
|
content = content.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search of single imports.
|
||||||
|
let regex = /@import[ ]*['"](.*)['"][ ]*;/g;
|
||||||
|
|
||||||
|
if (regex.test(content)) {
|
||||||
|
return content.replace(regex, (m, capture) => {
|
||||||
|
if (capture == "bmma") {
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.getReplace(capture, baseDir, paths, parsedFiles);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Search of multiple imports.
|
||||||
|
regex = /@import(?:[ \n]+['"](.*)['"][,]?[ \n]*)+;/gm;
|
||||||
|
if (regex.test(content)) {
|
||||||
|
return content.replace(regex, (m, capture) => {
|
||||||
|
let text = '';
|
||||||
|
|
||||||
|
// Divide the import into multiple files.
|
||||||
|
const captures = m.match(/['"]([^'"]*)['"]/g);
|
||||||
|
|
||||||
|
for (let x in captures) {
|
||||||
|
text += this.getReplace(captures[x].replace(/['"]+/g, ''), baseDir, paths, parsedFiles) + '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return text;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CombineScssTask;
|
|
@ -0,0 +1,79 @@
|
||||||
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const gulp = require('gulp');
|
||||||
|
const flatten = require('gulp-flatten');
|
||||||
|
const htmlmin = require('gulp-htmlmin');
|
||||||
|
const pathLib = require('path');
|
||||||
|
|
||||||
|
const TEMPLATES_SRC = [
|
||||||
|
'./src/components/**/*.html',
|
||||||
|
'./src/core/**/components/**/*.html',
|
||||||
|
'./src/core/**/component/**/*.html',
|
||||||
|
// Copy all addon components because any component can be injected using extraImports.
|
||||||
|
'./src/addon/**/components/**/*.html',
|
||||||
|
'./src/addon/**/component/**/*.html'
|
||||||
|
];
|
||||||
|
const TEMPLATES_DEST = './www/templates';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Task to copy component templates to www to make compile-html work in AOT.
|
||||||
|
*/
|
||||||
|
class CopyComponentTemplatesTask {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete a folder and all its contents.
|
||||||
|
*
|
||||||
|
* @param path [description]
|
||||||
|
* @return {[type]} [description]
|
||||||
|
*/
|
||||||
|
deleteFolderRecursive(path) {
|
||||||
|
if (fs.existsSync(path)) {
|
||||||
|
fs.readdirSync(path).forEach((file) => {
|
||||||
|
var curPath = pathLib.join(path, file);
|
||||||
|
|
||||||
|
if (fs.lstatSync(curPath).isDirectory()) {
|
||||||
|
this.deleteFolderRecursive(curPath);
|
||||||
|
} else {
|
||||||
|
fs.unlinkSync(curPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
fs.rmdirSync(path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the task.
|
||||||
|
*
|
||||||
|
* @param done Callback to call once done.
|
||||||
|
*/
|
||||||
|
run(done) {
|
||||||
|
this.deleteFolderRecursive(TEMPLATES_DEST);
|
||||||
|
|
||||||
|
gulp.src(TEMPLATES_SRC, { allowEmpty: true })
|
||||||
|
.pipe(flatten())
|
||||||
|
// Check options here: https://github.com/kangax/html-minifier
|
||||||
|
.pipe(htmlmin({
|
||||||
|
collapseWhitespace: true,
|
||||||
|
removeComments: true,
|
||||||
|
caseSensitive: true
|
||||||
|
}))
|
||||||
|
.pipe(gulp.dest(TEMPLATES_DEST))
|
||||||
|
.on('end', done);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CopyComponentTemplatesTask;
|
464
gulpfile.js
464
gulpfile.js
|
@ -1,437 +1,61 @@
|
||||||
var gulp = require('gulp'),
|
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||||
fs = require('fs'),
|
//
|
||||||
through = require('through'),
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
rename = require('gulp-rename'),
|
// you may not use this file except in compliance with the License.
|
||||||
path = require('path'),
|
// You may obtain a copy of the License at
|
||||||
slash = require('gulp-slash'),
|
//
|
||||||
clipEmptyFiles = require('gulp-clip-empty-files'),
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
File = require('vinyl'),
|
//
|
||||||
flatten = require('gulp-flatten'),
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
npmPath = require('path'),
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
concat = require('gulp-concat'),
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
htmlmin = require('gulp-htmlmin'),
|
// See the License for the specific language governing permissions and
|
||||||
bufferFrom = require('buffer-from'),
|
// limitations under the License.
|
||||||
exec = require('child_process').exec,
|
|
||||||
license = '' +
|
|
||||||
'// (C) Copyright 2015 Moodle Pty Ltd.\n' +
|
|
||||||
'//\n' +
|
|
||||||
'// Licensed under the Apache License, Version 2.0 (the "License");\n' +
|
|
||||||
'// you may not use this file except in compliance with the License.\n' +
|
|
||||||
'// You may obtain a copy of the License at\n' +
|
|
||||||
'//\n' +
|
|
||||||
'// http://www.apache.org/licenses/LICENSE-2.0\n' +
|
|
||||||
'//\n' +
|
|
||||||
'// Unless required by applicable law or agreed to in writing, software\n' +
|
|
||||||
'// distributed under the License is distributed on an "AS IS" BASIS,\n' +
|
|
||||||
'// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n' +
|
|
||||||
'// See the License for the specific language governing permissions and\n' +
|
|
||||||
'// limitations under the License.\n\n';
|
|
||||||
|
|
||||||
/**
|
const BuildConfigTask = require('./gulp/task-build-config');
|
||||||
* Copy a property from one object to another, adding a prefix to the key if needed.
|
const BuildLangTask = require('./gulp/task-build-lang');
|
||||||
* @param {Object} target Object to copy the properties to.
|
const CombineScssTask = require('./gulp/task-combine-scss');
|
||||||
* @param {Object} source Object to copy the properties from.
|
const CopyComponentTemplatesTask = require('./gulp/task-copy-component-templates');
|
||||||
* @param {String} prefix Prefix to add to the keys.
|
|
||||||
*/
|
|
||||||
function addProperties(target, source, prefix) {
|
|
||||||
for (var property in source) {
|
|
||||||
target[prefix + property] = source[property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
const gulp = require('gulp');
|
||||||
* Treats a file to merge JSONs. This function is based on gulp-jsoncombine module.
|
const pathLib = require('path');
|
||||||
* https://github.com/reflog/gulp-jsoncombine
|
|
||||||
* @param {Object} file File treated.
|
|
||||||
*/
|
|
||||||
function treatFile(file, data) {
|
|
||||||
if (file.isNull() || file.isStream()) {
|
|
||||||
return; // ignore
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
var srcPos = file.path.lastIndexOf('/src/');
|
|
||||||
if (srcPos == -1) {
|
|
||||||
// It's probably a Windows environment.
|
|
||||||
srcPos = file.path.lastIndexOf('\\src\\');
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = file.path.substr(srcPos + 5);
|
const paths = {
|
||||||
data[path] = JSON.parse(file.contents.toString());
|
lang: [
|
||||||
} catch (err) {
|
'./src/lang/',
|
||||||
console.log('Error parsing JSON: ' + err);
|
'./src/core/**/lang/',
|
||||||
}
|
'./src/addon/**/lang/',
|
||||||
}
|
'./src/assets/countries/',
|
||||||
|
'./src/assets/mimetypes/'
|
||||||
/**
|
],
|
||||||
* Treats the merged JSON data, adding prefixes depending on the component. Used in lang tasks.
|
config: './src/config.json',
|
||||||
*
|
};
|
||||||
* @param {Object} data Merged data.
|
|
||||||
* @return {Buffer} Buffer with the treated data.
|
|
||||||
*/
|
|
||||||
function treatMergedData(data) {
|
|
||||||
var merged = {};
|
|
||||||
var mergedOrdered = {};
|
|
||||||
|
|
||||||
for (var filepath in data) {
|
|
||||||
var pathSplit = filepath.split(/[\/\\]/),
|
|
||||||
prefix;
|
|
||||||
|
|
||||||
pathSplit.pop();
|
|
||||||
|
|
||||||
switch (pathSplit[0]) {
|
|
||||||
case 'lang':
|
|
||||||
prefix = 'core';
|
|
||||||
break;
|
|
||||||
case 'core':
|
|
||||||
if (pathSplit[1] == 'lang') {
|
|
||||||
// Not used right now.
|
|
||||||
prefix = 'core';
|
|
||||||
} else {
|
|
||||||
prefix = 'core.' + pathSplit[1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'addon':
|
|
||||||
// Remove final item 'lang'.
|
|
||||||
pathSplit.pop();
|
|
||||||
// Remove first item 'addon'.
|
|
||||||
pathSplit.shift();
|
|
||||||
|
|
||||||
// For subplugins. We'll use plugin_subfolder_subfolder2_...
|
|
||||||
// E.g. 'mod_assign_feedback_comments'.
|
|
||||||
prefix = 'addon.' + pathSplit.join('_');
|
|
||||||
break;
|
|
||||||
case 'assets':
|
|
||||||
prefix = 'assets.' + pathSplit[1];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefix) {
|
|
||||||
addProperties(merged, data[filepath], prefix + '.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force ordering by string key.
|
|
||||||
Object.keys(merged).sort().forEach(function(k){
|
|
||||||
mergedOrdered[k] = merged[k];
|
|
||||||
});
|
|
||||||
|
|
||||||
return bufferFrom(JSON.stringify(mergedOrdered, null, 4));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build lang file.
|
|
||||||
*
|
|
||||||
* @param {String} language Language to translate.
|
|
||||||
* @param {String[]} langPaths Paths to the possible language files.
|
|
||||||
* @param {String} buildDest Path where to leave the built files.
|
|
||||||
* @param {Function} done Function to call when done.
|
|
||||||
* @return {Void}
|
|
||||||
*/
|
|
||||||
function buildLang(language, langPaths, buildDest, done) {
|
|
||||||
var filename = language + '.json',
|
|
||||||
data = {},
|
|
||||||
firstFile = null;
|
|
||||||
|
|
||||||
var paths = langPaths.map(function(path) {
|
|
||||||
if (path.slice(-1) != '/') {
|
|
||||||
path = path + '/';
|
|
||||||
}
|
|
||||||
return path + language + '.json';
|
|
||||||
});
|
|
||||||
|
|
||||||
gulp.src(paths, { allowEmpty: true })
|
|
||||||
.pipe(slash())
|
|
||||||
.pipe(clipEmptyFiles())
|
|
||||||
.pipe(through(function(file) {
|
|
||||||
if (!firstFile) {
|
|
||||||
firstFile = file;
|
|
||||||
}
|
|
||||||
return treatFile(file, data);
|
|
||||||
}, function() {
|
|
||||||
/* This implementation is based on gulp-jsoncombine module.
|
|
||||||
* https://github.com/reflog/gulp-jsoncombine */
|
|
||||||
if (firstFile) {
|
|
||||||
var joinedPath = path.join(firstFile.base, language+'.json');
|
|
||||||
|
|
||||||
var joinedFile = new File({
|
|
||||||
cwd: firstFile.cwd,
|
|
||||||
base: firstFile.base,
|
|
||||||
path: joinedPath,
|
|
||||||
contents: treatMergedData(data)
|
|
||||||
});
|
|
||||||
|
|
||||||
this.emit('data', joinedFile);
|
|
||||||
}
|
|
||||||
this.emit('end');
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest(buildDest))
|
|
||||||
.on('end', done);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete a folder and all its contents.
|
|
||||||
function deleteFolderRecursive(path) {
|
|
||||||
if (fs.existsSync(path)) {
|
|
||||||
fs.readdirSync(path).forEach(function(file) {
|
|
||||||
var curPath = npmPath.join(path, file);
|
|
||||||
if (fs.lstatSync(curPath).isDirectory()) {
|
|
||||||
deleteFolderRecursive(curPath);
|
|
||||||
} else {
|
|
||||||
fs.unlinkSync(curPath);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.rmdirSync(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// List of app lang files. To be used only if cannot get it from filesystem.
|
|
||||||
var paths = {
|
|
||||||
src: './src',
|
|
||||||
assets: './src/assets',
|
|
||||||
lang: [
|
|
||||||
'./src/lang/',
|
|
||||||
'./src/core/**/lang/',
|
|
||||||
'./src/addon/**/lang/',
|
|
||||||
'./src/assets/countries/',
|
|
||||||
'./src/assets/mimetypes/'
|
|
||||||
],
|
|
||||||
config: './src/config.json',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build the language files into a single file per language.
|
// Build the language files into a single file per language.
|
||||||
gulp.task('lang', function(done) {
|
gulp.task('lang', (done) => {
|
||||||
buildLang('en', paths.lang, path.join(paths.assets, 'lang'), done);
|
new BuildLangTask().run('en', paths.lang, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Convert config.json into a TypeScript class.
|
// Convert config.json into a TypeScript class.
|
||||||
gulp.task('config', function(done) {
|
gulp.task('config', (done) => {
|
||||||
// Get the last commit.
|
new BuildConfigTask().run(paths.config, done);
|
||||||
exec('git log -1 --pretty=format:"%H"', function (err, commit, stderr) {
|
});
|
||||||
if (err) {
|
|
||||||
console.error('An error occurred while getting the last commit: ' + err);
|
|
||||||
} else if (stderr) {
|
|
||||||
console.error('An error occurred while getting the last commit: ' + stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.src(paths.config)
|
// Copy component templates to www to make compile-html work in AOT.
|
||||||
.pipe(through(function(file) {
|
gulp.task('copy-component-templates', (done) => {
|
||||||
// Convert the contents of the file into a TypeScript class.
|
new CopyComponentTemplatesTask().run(done);
|
||||||
// Disable the rule variable-name in the file.
|
});
|
||||||
var config = JSON.parse(file.contents.toString()),
|
|
||||||
contents = license + '// tslint:disable: variable-name\n' + 'export class CoreConfigConstants {\n',
|
|
||||||
that = this;
|
|
||||||
|
|
||||||
for (var key in config) {
|
// Combine SCSS files.
|
||||||
var value = config[key];
|
gulp.task('combine-scss', (done) => {
|
||||||
if (typeof value == 'string') {
|
new CombineScssTask().run(done);
|
||||||
// Wrap the string in ' and scape them.
|
|
||||||
value = "'" + value.replace(/([^\\])'/g, "$1\\'") + "'";
|
|
||||||
} else if (typeof value != 'number' && typeof value != 'boolean') {
|
|
||||||
// Stringify with 4 spaces of indentation, and then add 4 more spaces in each line.
|
|
||||||
value = JSON.stringify(value, null, 4).replace(/^(?: )/gm, ' ').replace(/^(?:})/gm, ' }');
|
|
||||||
// Replace " by ' in values.
|
|
||||||
value = value.replace(/: "([^"]*)"/g, ": '$1'");
|
|
||||||
|
|
||||||
// Check if the keys have "-" in it.
|
|
||||||
var matches = value.match(/"([^"]*\-[^"]*)":/g);
|
|
||||||
if (matches) {
|
|
||||||
// Replace " by ' in keys. We cannot remove them because keys have chars like '-'.
|
|
||||||
value = value.replace(/"([^"]*)":/g, "'$1':");
|
|
||||||
} else {
|
|
||||||
// Remove ' in keys.
|
|
||||||
value = value.replace(/"([^"]*)":/g, "$1:");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add type any to the key.
|
|
||||||
key = key + ': any';
|
|
||||||
}
|
|
||||||
|
|
||||||
// If key has quotation marks, remove them.
|
|
||||||
if (key[0] == '"') {
|
|
||||||
key = key.substr(1, key.length - 2);
|
|
||||||
}
|
|
||||||
contents += ' static ' + key + ' = ' + value + ';\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add compilation info.
|
|
||||||
contents += ' static compilationtime = ' + Date.now() + ';\n';
|
|
||||||
contents += ' static lastcommit = \'' + commit + '\';\n';
|
|
||||||
|
|
||||||
contents += '}\n';
|
|
||||||
|
|
||||||
file.contents = bufferFrom(contents);
|
|
||||||
this.emit('data', file);
|
|
||||||
}))
|
|
||||||
.pipe(rename('configconstants.ts'))
|
|
||||||
.pipe(gulp.dest(paths.src))
|
|
||||||
.on('end', done);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('default', gulp.parallel('lang', 'config'));
|
gulp.task('default', gulp.parallel('lang', 'config'));
|
||||||
|
|
||||||
gulp.task('watch', function() {
|
gulp.task('watch', () => {
|
||||||
var langsPaths = paths.lang.map(function(path) {
|
const langsPaths = paths.lang.map(path => path + 'en.json');
|
||||||
return path + 'en.json';
|
|
||||||
});
|
|
||||||
gulp.watch(langsPaths, { interval: 500 }, gulp.parallel('lang'));
|
gulp.watch(langsPaths, { interval: 500 }, gulp.parallel('lang'));
|
||||||
gulp.watch(paths.config, { interval: 500 }, gulp.parallel('config'));
|
gulp.watch(paths.config, { interval: 500 }, gulp.parallel('config'));
|
||||||
});
|
});
|
||||||
|
|
||||||
var templatesSrc = [
|
|
||||||
'./src/components/**/*.html',
|
|
||||||
'./src/core/**/components/**/*.html',
|
|
||||||
'./src/core/**/component/**/*.html',
|
|
||||||
// Copy all addon components because any component can be injected using extraImports.
|
|
||||||
'./src/addon/**/components/**/*.html',
|
|
||||||
'./src/addon/**/component/**/*.html'
|
|
||||||
],
|
|
||||||
templatesDest = './www/templates';
|
|
||||||
|
|
||||||
// Copy component templates to www to make compile-html work in AOT.
|
|
||||||
gulp.task('copy-component-templates', function(done) {
|
|
||||||
deleteFolderRecursive(templatesDest);
|
|
||||||
|
|
||||||
gulp.src(templatesSrc, { allowEmpty: true })
|
|
||||||
.pipe(flatten())
|
|
||||||
// Check options here: https://github.com/kangax/html-minifier
|
|
||||||
.pipe(htmlmin({
|
|
||||||
collapseWhitespace: true,
|
|
||||||
removeComments: true,
|
|
||||||
caseSensitive: true
|
|
||||||
}))
|
|
||||||
.pipe(gulp.dest(templatesDest))
|
|
||||||
.on('end', done);
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finds the file and returns its content.
|
|
||||||
*
|
|
||||||
* @param {string} capture Import file path.
|
|
||||||
* @param {string} baseDir Directory where the file was found.
|
|
||||||
* @param {string} paths Alternative paths where to find the imports.
|
|
||||||
* @param {Array} parsedFiles Yet parsed files to reduce size of the result.
|
|
||||||
* @return {string} Partially combined scss.
|
|
||||||
*/
|
|
||||||
function getReplace(capture, baseDir, paths, parsedFiles) {
|
|
||||||
var parse = path.parse(path.resolve(baseDir, capture + '.scss'));
|
|
||||||
var file = parse.dir + '/' + parse.name;
|
|
||||||
|
|
||||||
if (file.slice(-3) === '.wp') {
|
|
||||||
console.log('Windows Phone not supported "' + capture);
|
|
||||||
// File was already parsed, leave the import commented.
|
|
||||||
return '// @import "' + capture + '";';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(file + '.scss')) {
|
|
||||||
// File not found, might be a partial file.
|
|
||||||
file = parse.dir + '/_' + parse.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If file still not found, try to find the file in the alternative paths.
|
|
||||||
var x = 0;
|
|
||||||
while (!fs.existsSync(file + '.scss') && paths.length > x) {
|
|
||||||
parse = path.parse(path.resolve(paths[x], capture + '.scss'));
|
|
||||||
file = parse.dir + '/' + parse.name;
|
|
||||||
|
|
||||||
x++;
|
|
||||||
}
|
|
||||||
|
|
||||||
file = file + '.scss';
|
|
||||||
|
|
||||||
if (!fs.existsSync(file)) {
|
|
||||||
// File not found. Leave the import there.
|
|
||||||
console.log('File "' + capture + '" not found');
|
|
||||||
return '@import "' + capture + '";';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (parsedFiles.indexOf(file) >= 0) {
|
|
||||||
console.log('File "' + capture + '" already parsed');
|
|
||||||
// File was already parsed, leave the import commented.
|
|
||||||
return '// @import "' + capture + '";';
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedFiles.push(file);
|
|
||||||
var text = fs.readFileSync(file);
|
|
||||||
|
|
||||||
// Recursive call.
|
|
||||||
return scssCombine(text, parse.dir, paths, parsedFiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Combine scss files with its imports
|
|
||||||
*
|
|
||||||
* @param {string} content Scss string to read.
|
|
||||||
* @param {string} baseDir Directory where the file was found.
|
|
||||||
* @param {string} paths Alternative paths where to find the imports.
|
|
||||||
* @param {Array} parsedFiles Yet parsed files to reduce size of the result.
|
|
||||||
* @return {string} Scss string with the replaces done.
|
|
||||||
*/
|
|
||||||
function scssCombine(content, baseDir, paths, parsedFiles) {
|
|
||||||
|
|
||||||
// Content is a Buffer, convert to string.
|
|
||||||
if (typeof content != "string") {
|
|
||||||
content = content.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search of single imports.
|
|
||||||
var regex = /@import[ ]*['"](.*)['"][ ]*;/g;
|
|
||||||
|
|
||||||
if (regex.test(content)) {
|
|
||||||
return content.replace(regex, function(m, capture) {
|
|
||||||
if (capture == "bmma") {
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
return getReplace(capture, baseDir, paths, parsedFiles);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search of multiple imports.
|
|
||||||
regex = /@import(?:[ \n]+['"](.*)['"][,]?[ \n]*)+;/gm;
|
|
||||||
if (regex.test(content)) {
|
|
||||||
return content.replace(regex, function(m, capture) {
|
|
||||||
var text = "";
|
|
||||||
|
|
||||||
// Divide the import into multiple files.
|
|
||||||
regex = /['"]([^'"]*)['"]/g;
|
|
||||||
var captures = m.match(regex);
|
|
||||||
for (var x in captures) {
|
|
||||||
text += getReplace(captures[x].replace(/['"]+/g, ''), baseDir, paths, parsedFiles) + "\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
return text;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
|
|
||||||
gulp.task('combine-scss', function(done) {
|
|
||||||
var paths = [
|
|
||||||
'node_modules/ionic-angular/themes/',
|
|
||||||
'node_modules/font-awesome/scss/',
|
|
||||||
'node_modules/ionicons/dist/scss/'
|
|
||||||
];
|
|
||||||
|
|
||||||
var parsedFiles = [];
|
|
||||||
|
|
||||||
gulp.src([
|
|
||||||
'./src/theme/variables.scss',
|
|
||||||
'./node_modules/ionic-angular/themes/ionic.globals.*.scss',
|
|
||||||
'./node_modules/ionic-angular/themes/ionic.components.scss',
|
|
||||||
'./src/**/*.scss']) // define a source files
|
|
||||||
.pipe(through(function(file, encoding, callback) {
|
|
||||||
if (file.isNull()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
parsedFiles.push(file);
|
|
||||||
file.contents = bufferFrom(scssCombine(file.contents, path.dirname(file.path), paths, parsedFiles));
|
|
||||||
|
|
||||||
this.emit('data', file);
|
|
||||||
})) // combine them based on @import and save it to stream
|
|
||||||
.pipe(concat('combined.scss')) // concat the stream output in single file
|
|
||||||
.pipe(gulp.dest('.')) // save file to destination.
|
|
||||||
.on('end', done);
|
|
||||||
});
|
|
||||||
|
|
Loading…
Reference in New Issue