module.exports = function (grunt) {
var esperanto = require('esperanto');
var path = require('path');
var Promise = require('es6-promise').Promise;
var TMP_DIR = 'build/tmp';
function moveComments(code) {
var comments = [], rest = [];
code.split('\n').forEach(function (line) {
if (line.trim().slice(0, 3) === '//!') {
comments.push(line.trim());
} else {
rest.push(line);
}
});
return comments.concat([''], rest).join('\n');
}
var headerCache = {};
function getHeaderByFile(headerFile) {
if (!(headerFile in headerCache)) {
headerCache[headerFile] = grunt.file.read(headerFile);
}
return headerCache[headerFile];
}
function transpile(opts) {
// base, entry, skip, headerFile, skipLines, target
var umdName = opts.headerFile ? 'not_used' : opts.umdName,
header = opts.headerFile ? getHeaderByFile(opts.headerFile) : '',
skipLines = opts.skipLines ? opts.skipLines : 0;
return esperanto.bundle({
base: opts.base,
entry: opts.entry,
skip: opts.skip || []
}).then(function (bundle) {
var umd = bundle.toUmd({name: umdName}),
fixed = header + umd.code.split('\n').slice(skipLines).join('\n');
if (opts.moveComments) {
fixed = moveComments(fixed);
}
grunt.file.write(opts.target, fixed);
});
}
function transpileMany(opts) {
var batchSize = 50,
promise = Promise.resolve(null),
files = grunt.file.expand({cwd: opts.base}, opts.pattern),
i,
transpileOne = function (i) {
promise = promise.then(function () {
return Promise.all(files.slice(i, i + batchSize).map(function (file) {
return transpile({
base: opts.base,
entry: file,
headerFile: opts.headerFile,
skip: opts.skip,
skipLines: opts.skipLines,
moveComments: opts.moveComments,
target: path.join(opts.targetDir, file)
});
}));
});
};
for (i = 0; i < files.length; i += batchSize) {
transpileOne(i);
}
return promise;
}
function prepareTemp(base) {
var files = grunt.file.expand({cwd: base}, '**/*.js'),
tmpDir = TMP_DIR;
if (grunt.file.exists(tmpDir)) {
return;
}
files.forEach(function (file) {
grunt.file.copy(path.join(base, file), path.join(tmpDir, file));
});
}
function transpileCode(opts) {
var entry = opts.entry || path.basename(opts.target);
prepareTemp(opts.base);
grunt.file.write(path.join(TMP_DIR, entry), opts.code);
return transpile({
base: TMP_DIR,
entry: entry,
umdName: opts.umdName || 'not_used',
headerFile: opts.headerFile,
skipLines: opts.skipLines,
moveComments: opts.moveComments,
target: opts.target,
skip: opts.skip
});
}
function generateLocales(target, localeFiles) {
var files = localeFiles,
code = files.map(function (file) {
var identifier = path.basename(file, '.js').replace('-', '_');
return 'import ' + identifier + ' from "./' + file + '";';
}).join('\n');
return transpileCode({
base: 'src',
code: code,
target: target,
skip: ['moment'],
headerFile: 'templates/locale-header.js',
skipLines: 5
});
}
function generateMomentWithLocales(target, localeFiles) {
var files = localeFiles,
importCode = files.map(function (file) {
var identifier = path.basename(file, '.js').replace('-', '_');
var fileNoExt = file.replace('.js', '');
return 'import ' + identifier + ' from "./' + fileNoExt + '";';
}).join('\n'),
code = 'import * as moment_export from "./moment";\n\n' +
importCode + '\n\n' +
'export default moment_export;';
return transpileCode({
base: 'src',
code: code,
umdName: 'moment',
target: target
}).then(function () {
var code = grunt.file.read(target);
var getDefaultRegExp = new RegExp('var ([a-z$_]+) =\\s+{[^]\\s+get default \\(\\) { return ([a-z$_]+); }[^]\\s+}', '');
var crap = code.match(getDefaultRegExp);
if (crap.length !== 3) {
grunt.file.write('/tmp/crap.js', code);
throw new Error('Failed to detect get default crap, check /tmp/crap.js');
}
code = code.replace(getDefaultRegExp, '');
var buildExportVars = ['moment_with_locales', 'moment_with_locales_custom'];
buildExportVars.forEach(function (buildExportVar) {
var languageReset = buildExportVar + '.locale(\'en\');';
code = code.replace('var ' + buildExportVar + ' = ' + crap[1] + ';',
'var ' + buildExportVar + ' = ' + crap[2] + ';\n' +
' ' + languageReset);
});
if (code.match('get default')) {
grunt.file.write('/tmp/crap.js', code);
throw new Error('Stupid shit es6 get default plaguing the code, check /tmp/crap.js');
}
grunt.file.write(target, code);
});
}
grunt.task.registerTask('transpile-raw', 'convert es6 to umd', function () {
var done = this.async();
transpile({
base: 'src',
entry: 'moment.js',
umdName: 'moment',
target: 'build/umd/moment.js',
moveComments: true
}).then(function () {
grunt.log.ok('build/umd/moment.js');
}).then(function () {
return transpileMany({
base: 'src',
pattern: 'locale/*.js',
headerFile: 'templates/locale-header.js',
skipLines: 5,
moveComments: true,
targetDir: 'build/umd',
skip: ['moment']
});
}).then(function () {
grunt.log.ok('build/umd/locale/*.js');
}).then(function () {
return transpileMany({
base: 'src',
pattern: 'test/moment/*.js',
headerFile: 'templates/test-header.js',
skipLines: 5,
moveComments: true,
targetDir: 'build/umd',
skip: ['moment']
});
}).then(function () {
grunt.log.ok('build/umd/test/moment/*.js');
}).then(function () {
return transpileMany({
base: 'src',
pattern: 'test/locale/*.js',
headerFile: 'templates/test-header.js',
skipLines: 5,
moveComments: true,
targetDir: 'build/umd',
skip: ['moment']
});
}).then(function () {
grunt.log.ok('build/umd/test/locale/*.js');
}).then(function () {
return generateLocales('build/umd/min/locales.js',
grunt.file.expand({cwd: 'src'}, 'locale/*.js'));
}).then(function () {
grunt.log.ok('build/umd/min/locales.js');
}).then(function () {
return generateMomentWithLocales('build/umd/min/moment-with-locales.js',
grunt.file.expand({cwd: 'src'}, 'locale/*.js'));
}).then(function () {
grunt.log.ok('build/umd/min/moment-with-locales.js');
}).then(done, function (e) {
grunt.log.error('error transpiling', e);
done(e);
});
});
grunt.task.registerTask('transpile-custom-raw',
'build just custom language bundles',
function (locales) {
var done = this.async();
var localeFiles = locales.split(',').map(function (locale) {
var file = grunt.file.expand({cwd: 'src'}, 'locale/' + locale + '.js');
if (file.length !== 1) {
// we failed to find a locale
done(new Error('could not find locale: ' + locale));
done = null;
} else {
return file[0];
}
});
// There was an issue with a locale
if (done == null) {
return;
}
return generateLocales(
'build/umd/min/locales.custom.js', localeFiles
).then(function () {
grunt.log.ok('build/umd/min/locales.custom.js');
}).then(function () {
return generateMomentWithLocales('build/umd/min/moment-with-locales.custom.js',
localeFiles);
}).then(function () {
grunt.log.ok('build/umd/min/moment-with-locales.custom.js');
}).then(done, function (e) {
grunt.log.error('error transpiling-custom', e);
done(e);
});
});
grunt.config('clean.build', [
'build'
]);
grunt.config('concat.tests', {
src: 'build/umd/test/**/*.js',
dest: 'build/umd/min/tests.js'
});
grunt.task.registerTask('transpile',
'builds all es5 files, optinally creating custom locales',
function (locales) {
var tasks = [
'clean:build',
'transpile-raw',
'concat:tests'
];
if (locales) {
tasks.push('transpile-custom-raw:' + locales);
}
grunt.task.run(tasks);
});
};