lithium-sdk
Version:
### Lithium Developer Community Area
503 lines (459 loc) • 17.7 kB
JavaScript
/**
* Library methods for scripts tasks
*
* @author Nikhil Modak
*/
;
var lazyReq = require('lazy-req')(require);
var path = lazyReq('path');
var uglify = lazyReq('gulp-uglify');
var through = lazyReq('through2');
var is = lazyReq('is_js');
var plumber = lazyReq('gulp-plumber');
var ngAnnotate = lazyReq('gulp-ng-annotate');
var stringDecoder = lazyReq('string_decoder');
var gjshint = lazyReq('gulp-jshint');
var gjscs = lazyReq('gulp-jscs');
var wrap = lazyReq('gulp-wrap');
var gulpif = lazyReq('gulp-if');
var lazypipe = lazyReq('lazypipe');
var newer = lazyReq('gulp-newer');
var babel = lazyReq('gulp-babel');
var webpack = require('webpack-stream');
var fs = require('fs');
var rename = lazyReq('gulp-rename');
function getPrettyEscapedContent(templateContent) {
return templateContent
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'')
.replace(/\r?\n/g, '\\n\' +\n \'');
}
var HTML2JS_TEMPLATE = 'angular.module(\'<%= moduleName %>\').run([\'$templateCache\', function($templateCache) {\n' +
' $templateCache.put(\'<%= url %>\',\n \'<%= prettyEscapedContent %>\');\n' +
'}]);\n';
module.exports = function (gulp, gutil) {
const LIMUIRS_COMPONENT_PATH = 'limuirs/components/';
const COMPONENT_DEPS_SRC_PATH = gutil.env.newStructure ? 'limuirs/src/components' : 'limuirs/src/components';
const COMPONENT_DEPS_DEST_PATH = gutil.env.newStructure ? 'dist/plugin/res/feature/component-dependencies.json' : 'plugin/res/feature/component-dependencies.json';
var PLUGIN_SCRIPTS_PATH = gutil.env.newStructure ? 'dist/plugin/res/js/angularjs' : 'plugin/res/js/angularjs';
var SCRIPTS_DEPS_PATH = gutil.env.newStructure ? 'dist/plugin/res/js/angularjs/lib' : 'plugin/res/js/angularjs/lib';
var SCRIPTS_DEPS_METADATA_PATH = gutil.env.newStructure ? 'dist/plugin/res/js/angularjs/metadata' : 'plugin/res/js/angularjs/metadata';
var SCRIPT_DEPENDENCIES_PATH = gutil.env.newStructure ? 'dist/plugin/res/js/angularjs/metadata' : 'plugin/res/js/angularjs/metadata';
var JS_MAIN_PATTERN = gutil.env.newStructure ? 'angular-li/src/!(activecast|react-li)/**/!(*.demo|*.spec|*.mock).js' : 'src/!(activecast|react-li)/**/!(*.demo|*.spec|*.mock).js';
var JS_DEMO_PATTERN = gutil.env.newStructure ? 'angular-li/src/!(activecast|react-li)/**/*.demo.js' : 'src/!(activecast|react-li)/**/*.demo.js';
var JS_MOCK_PATTERN = gutil.env.newStructure ? 'angular-li/src/!(activecast|react-li)/**/*.mock.js' : 'src/!(activecast|react-li)/**/*.mock.js';
var JS_SPEC_PATTERN = gutil.env.newStructure ? 'angular-li/src/!(activecast|react-li)/**/*.spec.js' : "src/!(activecast|react-li)/**/*.spec.js";
var TPL_PATTERN = gutil.env.newStructure ? 'angular-li/src/!(activecast|react-li)/**/!(*.demo).tpl.html' : 'src/!(activecast|react-li)/**/!(*.demo).tpl.html';
var TPL_DEMO_PATTERN = 'directives/**/*.demo.tpl.html';
var BASE = gutil.env.newStructure ? 'angular-li/src' : 'src';
var EXPECTED_MODULE_PARTS = 4;
function processScripts(patterns, dest, files, isWatch, isLint) {
var fail = gutil.env['lint-fail'];
var stream = gulp.src(patterns)
.pipe(isWatch ? plumber()() : gutil.noop())
.pipe(isLint ? jshint(fail) : gutil.noop())
.pipe(isLint ? jscs(fail) : gutil.noop())
.pipe(newer()(dest))
.pipe(babel()({
presets: ['env']
}))
.pipe(wrapAngularContents())
.pipe(gutil.env.filterFiles(files))
.pipe(ngAnnotate()({
single_quotes: true
}))
.pipe(gulp.dest(dest));
return compress(stream, dest);
}
function compress(stream, dest) {
if (gutil.env.compress === true) {
stream
.pipe(uglify()({
compress: true,
mangle: true
}))
.pipe(rename()(function (path) {
path.basename += '.min';
}))
.pipe(gulp.dest(dest));
}
return stream;
}
/**
* bundles scripts based on patterns and puts it into dest with the specified destFilename.
*/
function processBundle(patterns, dest, destFilename, isWatch, isLint) {
var fail = gutil.env['lint-fail'];
return gulp.src(patterns)
.pipe(isWatch ? plumber()() : gutil.noop())
.pipe(isLint ? jshint(fail) : gutil.noop())
.pipe(isLint ? jscs(fail) : gutil.noop())
.pipe(newer()(dest))
.pipe(webpack({
watch: isWatch,
devtool: 'inline-source-map',
resolveLoader: {
modules: [path().join(__dirname, '/../../'), path().join(__dirname + '/../', 'node_modules')]
},
output: {
filename: destFilename
},
module: {
rules: [{
test: /\.js$/,
exclude:/\S*in-view\S*\.js$/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}]
}
}
))
.pipe(gulp.dest(dest));
}
function processTpls(pattern, dest, files, isWatch) {
var stream = gulp.src(pattern)
.pipe(newer()(dest))
.pipe(isWatch ? plumber()() : gutil.noop())
.pipe(gutil.env.filterFiles(files))
.pipe(_processTpls())
.pipe(wrapAngularContents())
.pipe(gulp.dest(dest));
return compress(stream, dest);
}
function jshint(fail) {
var pipe = lazypipe()()
.pipe(gjshint(), fs.existsSync('.jshintrc') ? '.jshintrc' : true)
.pipe(specialChecks)
.pipe(gjshint().reporter, 'jshint-stylish');
if (fail) {
pipe = pipe.pipe(gjshint().reporter, 'fail');
}
return pipe();
}
function jscs(fail) {
var pipe = lazypipe()();
var jscsrcPath = gutil.env.newStructure ? 'angular-li/.jscsrc' : '.jscsrc'
if (!fs.existsSync(jscsrcPath)) {
return pipe.pipe(gutil.noop)();
}
pipe = pipe
.pipe(gjscs(), fs.existsSync(jscsrcPath) ? { configPath: jscsrcPath } : {})
.pipe(gjscs().reporter);
if (fail) {
pipe = pipe.pipe(gjscs().reporter, 'fail');
}
return pipe();
}
function hackFormatDep(dep) {
var regex = gutil.env.newStructure ? /^(angular-li\/bower_components|node_modules)/ : /^(bower_components|node_modules)/;
return dep.replace(regex, '');
}
function createDepsMetadata(src, dest, isWatch) {
var moduleFiles = (function () {
var deps = {};
Object.keys(gutil.env.ng.moduleDependencyMap || {}).forEach(function (key) {
if (gutil.env.ng.useNewModuleDependenciesFormat === true) {
var moduleDeps = gutil.env.ng.moduleDependencyMap[key];
var moduleDevDependencyArray = [];
var moduleMinDependencyArray = [];
if (Array.isArray(moduleDeps)) {
moduleDeps.forEach(function (dep) {
return moduleDevDependencyArray.push(path().join('lib', hackFormatDep(dep)));
});
}
if ('dev' in moduleDeps && Array.isArray(moduleDeps.dev)) {
moduleDeps.dev.forEach(function (dep) {
return moduleDevDependencyArray.push(path().join('lib', hackFormatDep(dep)));
});
}
if ('min' in moduleDeps && Array.isArray(moduleDeps.min)) {
moduleDeps.min.forEach(function (dep) {
return moduleMinDependencyArray.push(path().join('lib', hackFormatDep(dep)));
});
}
deps[key] = {
dev: moduleDevDependencyArray,
min: moduleMinDependencyArray
};
} else {
deps[key] = gutil.env.ng.moduleDependencyMap[key].map(function (dep) {
return path().join('lib', hackFormatDep(dep));
});
}
});
return deps;
})();
var moduleDependencies = (function () {
var deps = {};
Object.keys(gutil.env.ng.moduleDependencyMap).forEach(function (key) {
if (key !== 'ng') {
deps[key] = ['ng'];
}
});
return deps;
})();
function recordModule(filePath, module, dependencies) {
if (dependencies !== undefined &&
moduleDependencies[module] !== undefined &&
JSON.stringify(moduleDependencies[module]) !== JSON.stringify(['ng'])) {
process.exitCode = 1;
throw new Error (
'Duplicate module declaration for [' +
module + '] in [' +
moduleFiles[module][0] + '] and [' +
filePath + ']');
} else {
/*
* Below logic checks whether file contains module declaration or lookup
* It makes sure to add the module declaration to the top of moduleFiles
* array
*/
if (gutil.env.ng.useNewModuleDependenciesFormat === true) {
/*
* Below logic checks whether file contains module declaration or lookup
* It makes sure to add the module declaration to the top of moduleFiles
* array
*/
if (dependencies !== undefined) {
/*
* found module declaration
* example: angular.module("my.module", ["some.dependencies"])
*/
if (moduleFiles[module] === undefined || moduleFiles[module].dev === undefined) {
moduleFiles[module] = { dev : [filePath], min : [filePath] };
} else {
moduleFiles[module].dev.push(filePath);
moduleFiles[module].min.push(filePath);
}
moduleDependencies[module] = is().empty(dependencies) ? ['ng'] : dependencies;
} else {
/*
* found module look up
* example: angular.module("my.module")
*/
if (moduleFiles[module] === undefined || moduleFiles[module].dev === undefined) {
moduleFiles[module] = { dev : [filePath], min : [filePath] };
} else {
if (moduleFiles[module].dev.indexOf(filePath) === -1) {
moduleFiles[module].dev.push(filePath);
moduleFiles[module].min.push(filePath);
}
}
}
} else {
if (dependencies !== undefined) {
/*
* found module declaration
* example: angular.module("my.module", ["some.dependencies"])
*/
moduleFiles[module] = [filePath].concat(moduleFiles[module] || []);
moduleDependencies[module] = is().empty(dependencies) ?
['ng'] : dependencies;
} else {
/*
* found module look up
* example: angular.module("my.module")
*/
if (moduleFiles[module] === undefined) {
moduleFiles[module] = [filePath];
} else {
if (moduleFiles[module].indexOf(filePath) === -1) {
moduleFiles[module].push(filePath);
}
}
}
}
}
}
return gulp.src(['**/!(min).js', '!lib/**/*.js'], {cwd: src})
.pipe(isWatch ? plumber()() : gutil.noop())
// Use Uglify to get the files in required format to be used in downstream function(createDepsMetadata)
.pipe(uglify()({compress: false, mangle: false}))
.pipe(through().obj(function (file, enc, cb) {
if (path().basename(file.path) === 'angular' || path().basename(file.path) === 'angular2' &&
path().extname(file.path) === 'js') {
cb();
return;
}
var module_declaration = /angular\.module\((.*?)\)/gi;
var contents = String(file.contents);
var results;
try {
while((results = module_declaration.exec(contents)) !== null) {
var moduleData = [file.path.substring(file.base.length)]
.concat(JSON.parse('[' + results[1] + ']'));
recordModule.apply(null, moduleData);
}
cb();
} catch (err) {
console.log(gutil.colors.red(err));
process.exit(1);
}
},
function (cb) {
if (Object.getOwnPropertyNames(moduleDependencies).length > 0) {
var file = new gutil.File({
base: '',
cwd: '',
path: 'dependencies.json'
});
file.contents = new Buffer(JSON.stringify({
moduleDependencies: sortObjectByPropertyName(moduleDependencies),
moduleFiles: sortObjectByPropertyName(moduleFiles)
}, null, 2));
this.push(file);
}
cb();
}))
.pipe(gulp.dest(dest));
}
function sortObjectByPropertyName(obj) {
return Object.keys(obj).sort().reduce(function (c, d) {
c[d] = obj[d];
return c;
}, {});
}
function specialChecks() {
function write(file, enc, callback) {
var StringDecoder = stringDecoder().StringDecoder;
var decoder = new StringDecoder('utf8');
var contents = decoder.write(file.contents).split('\n');
contents.forEach(function (line, i) {
var match = line.match(/[^\s(\/\'#\[\.>{\"]{/m);
if (match !== null) {
if (!file.jshint) {
file.jshint = {
success: false
};
}
if (!file.jshint.results) {
file.jshint.results = [];
}
file.jshint.results.push({
file: file.path,
error: {
id: '(error)',
raw: 'Contains \'{\' without a space, (, \', #, [, ., >, {, ", or \/ before it',
code: 'custom',
evidence: line,
line: i,
character: match.index,
scope: '(main)',
a: undefined,
b: undefined,
c: undefined,
d: undefined,
reason: 'Contains \'{\' without a space, (, \', #, [, ., >, {, ", or \/ before it'
}
});
}
});
callback(null, file);
}
return through().obj(write);
}
function _processTpls() {
return through().obj(function (file, enc, cb) {
/**
* Support tpl.html files outside of directives. If we encounter src/services in the
* current file path, set the url and moduleType to modify how we populate the HTML2JS_TEMPLATE
* template.
*/
var moduleType, url;
var joinPath = gutil.env.newStructure ? 'angular-li/src' : 'src';
if (file.path.match(/.*\/src\/services\/.*/)) {
/**
* If processing a service, substring at src to include 'service' in the
* url to provide uniqueness in the off-chance a service and directive ever happen to share
* the same name.
*/
url = file.path.substr(path().join(file.cwd, joinPath).length + 1);
moduleType = 'services';
} else {
url = file.path.substr(path().join(file.cwd, joinPath, 'directives').length + 1);
moduleType = 'directives';
}
file.contents = new Buffer(gutil.template(HTML2JS_TEMPLATE)({
moduleName: (function () {
var parts = file.path.split('/');
/**
* services/common/form-type-registry/fields/inputs/input.tpl.html
*
* The parts array is used to generate the moduleName for the templateCache entry. When
* subfolders are in the parts array, the moduleName incorrectly includes them, making the
* templateCache invalid. Look for the presence of sub-folders within the module by determining
* the number of path parts between the moduleType's index (service or directive) and the end of
* the path parts array. If there are more than 4, remove these from the array starting at the
* next-to-last position:
*
* parts (original)
* [ '', 'src', 'services', 'common', 'form-type-registry', 'fields', 'inputs', 'input.tpl.html' ]
*
* parts (modified)
* [ '', 'src', 'services', 'common', 'form-type-registry', 'input.tpl.html' ]
*
* From this, the moduleName will be correctly set as li.services.common.form-type-registry
*
**/
var numParts = parts.length;
var moduleTypeIndex = parts.indexOf(moduleType);
var diff = (numParts - moduleTypeIndex) - EXPECTED_MODULE_PARTS;
if (diff > 0) {
parts.splice((diff + 1) * -1, diff);
}
return [
gutil.env.ng.module,
moduleType,
parts[parts.length - 3],
parts[parts.length - 2]
].join('.');
})(),
prettyEscapedContent: getPrettyEscapedContent(String(file.contents)),
file: file,
url: url
}));
file.path = gutil.replaceExtension(file.path, '.js');
file.base = path().join(file.cwd, joinPath);
this.push(file);
cb();
});
}
function matchesAngularJs(file) {
return file.path.indexOf('angular/angular.js') !== -1;
}
function wrapAngularContents() {
var stream = through().obj();
return stream.pipe(gulpif()(
matchesAngularJs,
gutil.noop(),
wrap()(';(function (angular) {\n<%= contents %>\n})(window.LITHIUM && LITHIUM.angular || angular);'))
);
}
return {
processScripts,
processTpls,
processBundle,
jshint,
jscs,
createDepsMetadata,
specialChecks,
JS_MAIN_PATTERN,
JS_DEMO_PATTERN,
JS_MOCK_PATTERN,
JS_SPEC_PATTERN,
TPL_PATTERN,
TPL_DEMO_PATTERN,
PLUGIN_SCRIPTS_PATH,
SCRIPTS_DEPS_PATH,
SCRIPT_DEPENDENCIES_PATH,
SCRIPTS_DEPS_METADATA_PATH,
BASE,
LIMUIRS_COMPONENT_PATH,
COMPONENT_DEPS_SRC_PATH,
COMPONENT_DEPS_DEST_PATH
};
};