From 0d6d4a93268eea1523f38e3543cbef6939773743 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 22 Jul 2020 10:35:54 +0200 Subject: [PATCH] MOBILE-3501 gulp: Split gulpfile code into several classes --- gulp/task-build-config.js | 113 +++++++ gulp/task-build-lang.js | 176 ++++++++++ gulp/task-combine-scss.js | 164 +++++++++ gulp/task-copy-component-templates.js | 79 +++++ gulpfile.js | 464 +++----------------------- 5 files changed, 576 insertions(+), 420 deletions(-) create mode 100644 gulp/task-build-config.js create mode 100644 gulp/task-build-lang.js create mode 100644 gulp/task-combine-scss.js create mode 100644 gulp/task-copy-component-templates.js diff --git a/gulp/task-build-config.js b/gulp/task-build-config.js new file mode 100644 index 000000000..98d380140 --- /dev/null +++ b/gulp/task-build-config.js @@ -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; diff --git a/gulp/task-build-lang.js b/gulp/task-build-lang.js new file mode 100644 index 000000000..daa5d3126 --- /dev/null +++ b/gulp/task-build-lang.js @@ -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; diff --git a/gulp/task-combine-scss.js b/gulp/task-combine-scss.js new file mode 100644 index 000000000..0f0a28003 --- /dev/null +++ b/gulp/task-combine-scss.js @@ -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; diff --git a/gulp/task-copy-component-templates.js b/gulp/task-copy-component-templates.js new file mode 100644 index 000000000..2773a07b7 --- /dev/null +++ b/gulp/task-copy-component-templates.js @@ -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; diff --git a/gulpfile.js b/gulpfile.js index fbeead754..9f2e9499a 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,437 +1,61 @@ -var gulp = require('gulp'), - fs = require('fs'), - through = require('through'), - rename = require('gulp-rename'), - path = require('path'), - slash = require('gulp-slash'), - clipEmptyFiles = require('gulp-clip-empty-files'), - File = require('vinyl'), - flatten = require('gulp-flatten'), - npmPath = require('path'), - concat = require('gulp-concat'), - htmlmin = require('gulp-htmlmin'), - bufferFrom = require('buffer-from'), - 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'; +// (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. -/** - * Copy a property from one object to another, adding a prefix to the key if needed. - * @param {Object} target Object to copy the properties to. - * @param {Object} source Object to copy the properties from. - * @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 BuildConfigTask = require('./gulp/task-build-config'); +const BuildLangTask = require('./gulp/task-build-lang'); +const CombineScssTask = require('./gulp/task-combine-scss'); +const CopyComponentTemplatesTask = require('./gulp/task-copy-component-templates'); -/** - * Treats a file to merge JSONs. This function is based on gulp-jsoncombine module. - * 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\\'); - } +const gulp = require('gulp'); +const pathLib = require('path'); - var 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. Used in lang tasks. - * - * @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', - }; +const paths = { + 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. -gulp.task('lang', function(done) { - buildLang('en', paths.lang, path.join(paths.assets, 'lang'), done); +gulp.task('lang', (done) => { + new BuildLangTask().run('en', paths.lang, done); }); // Convert config.json into a TypeScript class. -gulp.task('config', function(done) { - // Get the last commit. - 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.task('config', (done) => { + new BuildConfigTask().run(paths.config, done); +}); - gulp.src(paths.config) - .pipe(through(function(file) { - // Convert the contents of the file into a TypeScript class. - // 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; +// Copy component templates to www to make compile-html work in AOT. +gulp.task('copy-component-templates', (done) => { + new CopyComponentTemplatesTask().run(done); +}); - for (var key in config) { - var value = config[key]; - if (typeof value == 'string') { - // 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); - }); +// Combine SCSS files. +gulp.task('combine-scss', (done) => { + new CombineScssTask().run(done); }); gulp.task('default', gulp.parallel('lang', 'config')); -gulp.task('watch', function() { - var langsPaths = paths.lang.map(function(path) { - return path + 'en.json'; - }); +gulp.task('watch', () => { + const langsPaths = paths.lang.map(path => path + 'en.json'); + gulp.watch(langsPaths, { interval: 500 }, gulp.parallel('lang')); 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); -});