/** * @license * Copyright 2018 Google LLC * * 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. */ /** * @fileoverview Gulp script to build Blockly for Node & NPM. * Run this script by calling "npm install" in this directory. */ var gulp = require('gulp'); gulp.shell = require('gulp-shell'); gulp.concat = require('gulp-concat'); gulp.replace = require('gulp-replace'); gulp.rename = require('gulp-rename'); gulp.insert = require('gulp-insert'); gulp.umd = require('gulp-umd'); var path = require('path'); var fs = require('fs'); var rimraf = require('rimraf'); var execSync = require('child_process').execSync; var through2 = require('through2'); var closureCompiler = require('google-closure-compiler').gulp(); var closureDeps = require('google-closure-deps'); var packageJson = require('./package.json'); var argv = require('yargs').argv; const upstream_url = "https://github.com/google/blockly.git"; //////////////////////////////////////////////////////////// // Build // //////////////////////////////////////////////////////////// const licenseRegex = `\\/\\*\\* \\* @license \\* (Copyright \\d+ (Google LLC|Massachusetts Institute of Technology)) ( \\* All rights reserved. )? \\* \\* 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. \\*\\/`; /** * Helper method for stripping the Google's and MIT's Apache Licenses. */ function stripApacheLicense() { // Strip out Google's and MIT's Apache licences. // Closure Compiler preserves dozens of Apache licences in the Blockly code. // Remove these if they belong to Google or MIT. // MIT's permission to do this is logged in Blockly issue #2412. return gulp.replace(new RegExp(licenseRegex, "g"), ''); } /** * Helper method for prepending the auto-generated header text. */ function prependHeader() { return gulp.insert.prepend(`// Do not edit this file; automatically generated by gulp.\n`); } /** * Closure compiler warning groups used to treat warnings as errors. * For a full list of closure compiler groups, consult: * https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/DiagnosticGroups.java#L113 */ var JSCOMP_ERROR = [ 'accessControls', 'checkPrototypalTypes', 'checkRegExp', 'checkTypes', 'checkVars', 'conformanceViolations', 'const', 'constantProperty', 'deprecated', 'deprecatedAnnotations', 'duplicateMessage', 'es5Strict', 'externsValidation', 'functionParams', 'globalThis', 'invalidCasts', 'misplacedTypeAnnotation', 'missingGetCssName', // 'missingOverride', 'missingPolyfill', 'missingProperties', 'missingProvide', 'missingRequire', 'missingReturn', // 'missingSourcesWarnings', 'moduleLoad', 'msgDescriptions', 'nonStandardJsDocs', // 'polymer', // 'reportUnknownTypes', // 'strictCheckTypes', // 'strictMissingProperties', 'strictModuleDepCheck', // 'strictPrimitiveOperators', 'suspiciousCode', 'typeInvalidation', 'undefinedNames', 'undefinedVars', 'underscore', 'unknownDefines', 'unusedLocalVariables', // 'unusedPrivateMembers', 'useOfGoogBase', 'uselessCode', 'untranspilableFeatures', 'visibility' ]; /** * Helper method for calling the Closure compiler. * @param {*} compilerOptions * @param {boolean=} opt_verbose Optional option for verbose logging * @param {boolean=} opt_warnings_as_error Optional option for treating warnings * as errors. */ function compile(compilerOptions, opt_verbose, opt_warnings_as_error) { compilerOptions = compilerOptions || {}; compilerOptions.compilation_level = 'SIMPLE_OPTIMIZATIONS'; compilerOptions.warning_level = opt_verbose ? 'VERBOSE' : 'DEFAULT'; compilerOptions.language_in = compilerOptions.language_in || 'ECMASCRIPT5_STRICT'; compilerOptions.language_out = 'ECMASCRIPT5_STRICT'; compilerOptions.rewrite_polyfills = false; compilerOptions.hide_warnings_for = 'node_modules'; if (opt_warnings_as_error) { compilerOptions.jscomp_error = JSCOMP_ERROR; } const platform = ['native', 'java', 'javascript']; return closureCompiler(compilerOptions, { platform }); } /** * This task builds Blockly's core files. * blockly_compressed.js */ gulp.task('build-compressed', function (cb) { const defines = 'Blockly.VERSION="' + packageJson.version + '"'; const srcs = ['core/**/**/*.js']; if (argv.closureLibrary) { // If you require the google closure library, you can include it in your // build by running: // gulp build-compressed --closure-library // You will also need to include the "google-closure-library" in your list // of devDependencies. console.log('Including the google-closure-library in your build.'); if (!fs.existsSync('./node_modules/google-closure-library')) { throw Error('You must add the google-closure-library to your ' + 'devDependencies in package.json, and run `npm install`.'); } srcs.push('./node_modules/google-closure-library/closure/goog/**/*.js'); } return gulp.src(srcs, {base: './'}) // Directories in Blockly are used to group similar files together // but are not used to limit access with @package, instead the // method means something is internal to Blockly and not a public // API. // Flatten all files so they're in the same directory, but ensure that // files with the same name don't conflict. .pipe(gulp.rename(function (p) { var dirname = p.dirname.replace(new RegExp(path.sep, "g"), "-"); p.dirname = ""; p.basename = dirname + "-" + p.basename; })) .pipe(stripApacheLicense()) .pipe(compile({ dependency_mode: 'PRUNE', entry_point: './core-requires.js', js_output_file: 'blockly_compressed.js', externs: ['./externs/svg-externs.js', './externs/goog-externs.js'], define: defines, language_in: argv.closureLibrary ? 'ECMASCRIPT_2015' : 'ECMASCRIPT5_STRICT' }, argv.verbose, argv.strict)) .pipe(prependHeader()) .pipe(gulp.dest('./')); }); /** * This task builds the Blockly's built in blocks. * blocks_compressed.js */ gulp.task('build-blocks', function () { // Add provides used throughout blocks/ in order to be compatible with the // compiler. Anything added to this list must be removed from the compiled // result using the remove regex steps below. const provides = ` goog.provide('Blockly'); goog.provide('Blockly.Blocks'); goog.provide('Blockly.Comment'); goog.provide('Blockly.FieldCheckbox'); goog.provide('Blockly.FieldColour'); goog.provide('Blockly.FieldDropdown'); goog.provide('Blockly.FieldImage'); goog.provide('Blockly.FieldLabel'); goog.provide('Blockly.FieldMultilineInput'); goog.provide('Blockly.FieldNumber'); goog.provide('Blockly.FieldTextInput'); goog.provide('Blockly.FieldVariable'); goog.provide('Blockly.Mutator'); goog.provide('Blockly.Warning');`; return gulp.src('blocks/*.js', {base: './'}) // Add Blockly.Blocks to be compatible with the compiler. .pipe(gulp.replace(`goog.provide('Blockly.Constants.Colour');`, `${provides}goog.provide('Blockly.Constants.Colour');`)) .pipe(stripApacheLicense()) .pipe(compile({ dependency_mode: 'NONE', externs: ['./externs/goog-externs.js'], js_output_file: 'blocks_compressed.js' }, argv.verbose, argv.strict)) .pipe(gulp.replace('\'use strict\';', '\'use strict\';\n\n\n')) // Remove Blockly.Blocks to be compatible with Blockly. .pipe(gulp.replace(/var Blockly=\{[^;]*\};\n?/, '')) // Remove Blockly Fields to be compatible with Blockly. .pipe(gulp.replace(/Blockly\.Field[^=\(]+=\{[^;]*\};/g, '')) // Remove Blockly Warning, Comment & Mutator to be compatible with Blockly. .pipe(gulp.replace(/Blockly\.(Comment|Warning|Mutator)=\{[^;]*\};/g, '')) .pipe(prependHeader()) .pipe(gulp.dest('./')); }); /** * A helper method for building a Blockly code generator. * @param {string} language Generator language. * @param {string} namespace Language namespace. */ function buildGenerator(language, namespace) { var provides = ` goog.provide('Blockly.Generator'); goog.provide('Blockly.utils.global'); goog.provide('Blockly.utils.string');`; return gulp.src([`generators/${language}.js`, `generators/${language}/*.js`], {base: './'}) .pipe(stripApacheLicense()) // Add Blockly.Generator and Blockly.utils.string to be compatible with the compiler. .pipe(gulp.replace(`goog.provide('Blockly.${namespace}');`, `${provides}goog.provide('Blockly.${namespace}');`)) .pipe(compile({ dependency_mode: 'NONE', externs: ['./externs/goog-externs.js'], js_output_file: `${language}_compressed.js` }, argv.verbose, argv.strict)) .pipe(gulp.replace('\'use strict\';', '\'use strict\';\n\n\n')) // Remove Blockly.Generator and Blockly.utils.string to be compatible with Blockly. .pipe(gulp.replace(/var Blockly=\{[^;]*\};\s*Blockly.utils.global={};\s*Blockly.utils.string={};\n?/, '')) .pipe(prependHeader()) .pipe(gulp.dest('./')); }; /** * This task builds the javascript generator. * javascript_compressed.js */ gulp.task('build-javascript', function() { return buildGenerator('javascript', 'JavaScript'); }); /** * This task builds the python generator. * python_compressed.js */ gulp.task('build-python', function() { return buildGenerator('python', 'Python'); }); /** * This task builds the php generator. * php_compressed.js */ gulp.task('build-php', function() { return buildGenerator('php', 'PHP'); }); /** * This task builds the lua generator. * lua_compressed.js */ gulp.task('build-lua', function() { return buildGenerator('lua', 'Lua'); }); /** * This task builds the dart generator: * dart_compressed.js */ gulp.task('build-dart', function() { return buildGenerator('dart', 'Dart'); }); /** * This tasks builds all the generators: * javascript_compressed.js * python_compressed.js * php_compressed.js * lua_compressed.js * dart_compressed.js */ gulp.task('build-generators', gulp.parallel( 'build-javascript', 'build-python', 'build-php', 'build-lua', 'build-dart' )); /** * This task builds Blockly's uncompressed file. * blockly_uncompressed.js */ gulp.task('build-uncompressed', function() { const header = `// Do not edit this file; automatically generated by gulp. 'use strict'; this.IS_NODE_JS = !!(typeof module !== 'undefined' && module.exports); this.BLOCKLY_DIR = (function(root) { if (!root.IS_NODE_JS) { // Find name of current directory. var scripts = document.getElementsByTagName('script'); var re = new RegExp('(.+)[\\\/]blockly_(.*)uncompressed\\\.js$'); for (var i = 0, script; script = scripts[i]; i++) { var match = re.exec(script.src); if (match) { return match[1]; } } alert('Could not detect Blockly\\'s directory name.'); } return ''; })(this); this.BLOCKLY_BOOT = function(root) { // Execute after Closure has loaded. `; const footer = ` delete root.BLOCKLY_DIR; delete root.BLOCKLY_BOOT; delete root.IS_NODE_JS; }; if (this.IS_NODE_JS) { this.BLOCKLY_BOOT(this); module.exports = Blockly; } else { document.write(''); document.write(''); } `; let deps = []; return gulp.src('core/**/**/*.js') .pipe(through2.obj((file, _enc, cb) => { deps.push(closureDeps.parser.parseFile(file.path).dependencies[0]); cb(null); })) .on('end', () => { const graph = new closureDeps.depGraph.Graph(deps); let addDependency = []; graph.depsByPath.forEach(dep => { addDependency.push('goog.addDependency(' + [ '"' + path.relative('./closure/goog', dep.path) + '"', '[' + dep.closureSymbols .map(s => `'${s}'`).join(', ') + ']', '[' + dep.imports .map(i => i.symOrPath) .filter(i => i !== 'goog') .sort() .map(i => `'${i}'`).join(', ') + ']', ].join(', ') + ');'); }); const requires = ` goog.addDependency("base.js", [], []); // Load Blockly. goog.require('Blockly.requires') `; fs.writeFileSync('blockly_uncompressed.js', header + addDependency.sort((a, b) => a.localeCompare(b, undefined, {sensitivity: 'base'})).join('\n') + requires + footer); }); }); /** * This task builds Blockly's lang files. * msg/*.js */ gulp.task('build-langfiles', function(done) { // Run js_to_json.py const jsToJsonCmd = `python ./i18n/js_to_json.py \ --input_file ${path.join('msg', 'messages.js')} \ --output_dir ${path.join('msg', 'json')} \ --quiet`; execSync(jsToJsonCmd, { stdio: 'inherit' }); // Run create_messages.py let json_files = fs.readdirSync(path.join('msg', 'json')); json_files = json_files.filter(file => file.endsWith('json') && !(new RegExp(/(keys|synonyms|qqq|constants)\.json$/).test(file))); json_files = json_files.map(file => path.join('msg', 'json', file)); const createMessagesCmd = `python ./i18n/create_messages.py \ --source_lang_file ${path.join('msg', 'json', 'en.json')} \ --source_synonym_file ${path.join('msg', 'json', 'synonyms.json')} \ --source_constants_file ${path.join('msg', 'json', 'constants.json')} \ --key_file ${path.join('msg', 'json', 'keys.json')} \ --output_dir ${path.join('msg', 'js')} \ --quiet ${json_files.join(' ')}`; execSync(createMessagesCmd, { stdio: 'inherit' }); done(); }); /** * This tasks builds Blockly's core files: * blockly_compressed.js * blocks_compressed.js * blockly_uncompressed.js */ gulp.task('build-core', gulp.parallel( 'build-compressed', 'build-blocks', 'build-uncompressed' )); /** * This task builds all of Blockly: * blockly_compressed.js * blocks_compressed.js * javascript_compressed.js * python_compressed.js * php_compressed.js * lua_compressed.js * dart_compressed.js * blockly_uncompressed.js * msg/json/*.js */ gulp.task('build', gulp.parallel( 'build-core', 'build-generators', 'build-langfiles' )); //////////////////////////////////////////////////////////// // Typings // //////////////////////////////////////////////////////////// // Generates the TypeScript definition file (d.ts) for Blockly. // As well as generating the typings of each of the files under core/ and msg/, // the script also pulls in a number of part files from typings/parts. // This includes the header (incl License), additional useful interfaces // including Blockly Options and Google Closure typings. gulp.task('typings', function (cb) { const tmpDir = './typings/tmp'; const blocklySrcs = [ "core/", "core/components", "core/components/tree", "core/components/menu", "core/keyboard_nav", "core/renderers/common", "core/renderers/measurables", "core/theme", "core/utils", "msg/" ]; // Clean directory if exists. if (fs.existsSync(tmpDir)) { rimraf.sync(tmpDir); } fs.mkdirSync(tmpDir); // Find all files that will be included in the typings file. let files = []; blocklySrcs.forEach((src) => { files = files.concat(fs.readdirSync(src) .filter(fn => fn.endsWith('.js')) .map(fn => path.join(src, fn))); }); // Generate typings file for each file. files.forEach((file) => { const typescriptFileName = `${path.join(tmpDir, file)}.d.ts`; if (file.indexOf('core/msg.js') > -1) { return; } const cmd = `node ./node_modules/typescript-closure-tools/definition-generator/src/main.js ${file} ${typescriptFileName}`; console.log(`Generating typings for ${file}`); execSync(cmd, { stdio: 'inherit' }); }); const srcs = [ 'typings/parts/blockly-header.d.ts', 'typings/parts/blockly-interfaces.d.ts', `${tmpDir}/core/**`, `${tmpDir}/core/components/**`, `${tmpDir}/core/components/tree/**`, `${tmpDir}/core/components/menu/**`, `${tmpDir}/core/keyboard_nav/**`, `${tmpDir}/core/renderers/common/**`, `${tmpDir}/core/renderers/measurables/**`, `${tmpDir}/core/utils/**`, `${tmpDir}/core/theme/**`, `${tmpDir}/msg/**` ]; return gulp.src(srcs) .pipe(gulp.concat('blockly.d.ts')) .pipe(gulp.dest('typings')) .on('end', function () { // Clean up tmp directory. if (fs.existsSync(tmpDir)) { rimraf.sync(tmpDir); } }); }); //////////////////////////////////////////////////////////// // NPM packaging tasks // //////////////////////////////////////////////////////////// // The destination path where all the NPM distribution files will go. const packageDistribution = './dist'; /** * A helper method for wrapping a file into a Universal Module Definition. * @param {string} namespace The export namespace. * @param {Array} dependencies An array of dependencies to inject. */ function packageUMD(namespace, dependencies) { return gulp.umd({ dependencies: function () { return dependencies; }, namespace: function () { return namespace; }, exports: function () { return namespace; }, template: path.join(__dirname, 'package/templates/umd.template') }); }; /** * A helper method for wrapping a file into a CommonJS module for Node.js. * @param {string} namespace The export namespace. * @param {Array} dependencies An array of dependencies to inject. */ function packageCommonJS(namespace, dependencies) { return gulp.umd({ dependencies: function () { return dependencies; }, namespace: function () { return namespace; }, exports: function () { return namespace; }, template: path.join(__dirname, 'package/templates/node.template') }); }; /** * This task wraps blockly_compressed.js into a UMD module. * @example import 'blockly/blockly'; */ gulp.task('package-blockly', function() { return gulp.src('blockly_compressed.js') .pipe(packageUMD('Blockly', [])) .pipe(gulp.rename('blockly.js')) .pipe(gulp.dest(packageDistribution)); }); /** * This task wraps blocks_compressed.js into a CommonJS module for Node.js. * This is an equivelant task to package-blockly but for Node.js. * @example import 'blockly/blockly-node'; */ gulp.task('package-blockly-node', function() { // Override textToDomDocument, providing a Node.js alternative to DOMParser. return gulp.src('blockly_compressed.js') .pipe(gulp.insert.append(` if (typeof DOMParser !== 'function') { var DOMParser = require("jsdom/lib/jsdom/living").DOMParser; var XMLSerializer = require("jsdom/lib/jsdom/living").XMLSerializer; var doc = Blockly.utils.xml.textToDomDocument( ''); Blockly.utils.xml.document = function() { return doc; }; }`)) .pipe(packageCommonJS('Blockly', [])) .pipe(gulp.rename('blockly-node.js')) .pipe(gulp.dest(packageDistribution)); }) /** * This task wraps blocks_compressed.js into a UMD module. * @example import 'blockly/blocks'; */ gulp.task('package-blocks', function() { return gulp.src('blocks_compressed.js') .pipe(gulp.insert.prepend(` Blockly.Blocks={};`)) .pipe(packageUMD('Blockly.Blocks', [{ name: 'Blockly', amd: './core', cjs: './core', }])) .pipe(gulp.rename('blocks.js')) .pipe(gulp.dest(packageDistribution)); }); /** * This task wraps package/index.js into a UMD module. * We implicitly require the Node entry point in CommonJS environments, * and the Browser entry point for AMD environments. * @example import * as Blockly from 'blockly'; */ gulp.task('package-index', function() { return gulp.src('package/index.js') .pipe(packageUMD('Blockly', [{ name: 'Blockly', amd: './browser', cjs: './node', }])) .pipe(gulp.rename('index.js')) .pipe(gulp.dest(packageDistribution)); }); /** * This task wraps package/browser/index.js into a UMD module. * By default, the module includes Blockly core and built-in blocks, * as well as the JavaScript code generator and the English block * localization files. * This module is configured (in package.json) to replaces the module * built by package-node in browser environments. * @example import * as Blockly from 'blockly/browser'; */ gulp.task('package-browser', function() { return gulp.src('package/browser/index.js') .pipe(packageUMD('Blockly', [{ name: 'Blockly', amd: './core-browser', cjs: './core-browser', },{ name: 'En', amd: './msg/en', cjs: './msg/en', },{ name: 'BlocklyBlocks', amd: './blocks', cjs: './blocks', },{ name: 'BlocklyJS', amd: './javascript', cjs: './javascript', }])) .pipe(gulp.rename('browser.js')) .pipe(gulp.dest(packageDistribution)); }); /** * This task wraps package/browser/core.js into a UMD module. * By default, the module includes the Blockly core package and a * helper method to set the locale. * This module is configured (in package.json) to replaces the module * built by package-node-core in browser environments. * @example import * as Blockly from 'blockly/core'; */ gulp.task('package-core', function() { return gulp.src('package/browser/core.js') .pipe(packageUMD('Blockly', [{ name: 'Blockly', amd: './blockly', cjs: './blockly', }])) .pipe(gulp.rename('core-browser.js')) .pipe(gulp.dest(packageDistribution)); }); /** * This task wraps package/node/index.js into a CommonJS module for Node.js. * By default, the module includes Blockly core and built-in blocks, * as well as all the code generators and the English block localization files. * This module is configured (in package.json) to be replaced by the module * built by package-browser in browser environments. * @example import * as Blockly from 'blockly/node'; */ gulp.task('package-node', function() { return gulp.src('package/node/index.js') .pipe(packageCommonJS('Blockly', [{ name: 'Blockly', cjs: './core', },{ name: 'En', cjs: './msg/en', },{ name: 'BlocklyBlocks', cjs: './blocks', },{ name: 'BlocklyJS', cjs: './javascript', },{ name: 'BlocklyPython', cjs: './python', },{ name: 'BlocklyPHP', cjs: './php', },{ name: 'BlocklyLua', cjs: './lua', }, { name: 'BlocklyDart', cjs: './dart', }])) .pipe(gulp.rename('node.js')) .pipe(gulp.dest(packageDistribution)); }); /** * This task wraps package/node/core.js into a CommonJS module for Node.js. * By default, the module includes the Blockly core package for Node.js * and a helper method to set the locale. * This module is configured (in package.json) to be replaced by the module * built by package-core in browser environments. * @example import * as Blockly from 'blockly/core'; */ gulp.task('package-node-core', function() { return gulp.src('package/node/core.js') .pipe(packageCommonJS('Blockly', [{ name: 'Blockly', amd: './blockly-node', cjs: './blockly-node', }])) .pipe(gulp.rename('core.js')) .pipe(gulp.dest(packageDistribution)); }); /** * A helper method for packaging a Blockly code generator into a UMD module. * @param {string} file Source file name. * @param {string} rename Destination file name. * @param {string} generator Generator export namespace. */ function packageGenerator(file, rename, generator) { return gulp.src(file) .pipe(packageUMD(generator, [{ name: 'Blockly', amd: './core', cjs: './core', }])) .pipe(gulp.rename(rename)) .pipe(gulp.dest(packageDistribution)); }; /** * This task wraps javascript_compressed.js into a UMD module. * @example import 'blockly/javascript'; */ gulp.task('package-javascript', function() { return packageGenerator('javascript_compressed.js', 'javascript.js', 'Blockly.JavaScript'); }); /** * This task wraps python_compressed.js into a UMD module. * @example import 'blockly/python'; */ gulp.task('package-python', function() { return packageGenerator('python_compressed.js', 'python.js', 'Blockly.Python'); }); /** * This task wraps lua_compressed.js into a UMD module. * @example import 'blockly/lua'; */ gulp.task('package-lua', function() { return packageGenerator('lua_compressed.js', 'lua.js', 'Blockly.Lua'); }); /** * This task wraps dart_compressed.js into a UMD module. * @example import 'blockly/dart'; */ gulp.task('package-dart', function() { return packageGenerator('dart_compressed.js', 'dart.js', 'Blockly.Dart'); }); /** * This task wraps php_compressed.js into a UMD module. * @example import 'blockly/php'; */ gulp.task('package-php', function() { return packageGenerator('php_compressed.js', 'php.js', 'Blockly.PHP'); }); /** * This task wraps each of the msg/js/* files into a UMD module. * @example import * as En from 'blockly/msg/en'; */ gulp.task('package-locales', function() { // Remove references to goog.provide and goog.require. return gulp.src('msg/js/*.js') .pipe(gulp.replace(/goog\.[^\n]+/g, '')) .pipe(gulp.insert.prepend(` var Blockly = {};Blockly.Msg={};`)) .pipe(packageUMD('Blockly.Msg', [{ name: 'Blockly', amd: '../core', cjs: '../core', }])) .pipe(gulp.dest(`${packageDistribution}/msg`)); }); /** * This task creates a UMD bundle of Blockly which includes the Blockly * core files, the built-in blocks, the JavaScript code generator and the * English localization files. * @example */ gulp.task('package-umd-bundle', function() { var srcs = [ 'blockly_compressed.js', 'msg/js/en.js', 'blocks_compressed.js', 'javascript_compressed.js' ]; return gulp.src(srcs) .pipe(gulp.concat('blockly.min.js')) .pipe(packageUMD('Blockly', [])) .pipe(gulp.dest(`${packageDistribution}`)) }); /** * This task copies all the media/* files into the distribution directory. */ gulp.task('package-media', function() { return gulp.src('./media/*') .pipe(gulp.dest(`${packageDistribution}/media`)); }); /** * This task copies the package.json file into the distribution directory. */ gulp.task('package-json', function() { return gulp.src('./package.json') .pipe(gulp.dest(`${packageDistribution}`)) }); /** * This task copies the package/README.md file into the distribution directory. * This file is what developers will see at https://www.npmjs.com/package/blockly. */ gulp.task('package-readme', function() { return gulp.src('./package/README.md') .pipe(gulp.dest(`${packageDistribution}`)) }); /** * This task copies the typings/blockly.d.ts TypeScript definition file into the * distribution directory. * The bundled declaration file is referenced in package.json in the types property. */ gulp.task('package-dts', function() { return gulp.src('./typings/blockly.d.ts') .pipe(gulp.dest(`${packageDistribution}`)) }); /** * This task prepares the NPM distribution files under the /dist directory. */ gulp.task('package', gulp.parallel( 'package-index', 'package-browser', 'package-node', 'package-core', 'package-node-core', 'package-blockly', 'package-blockly-node', 'package-blocks', 'package-javascript', 'package-python', 'package-lua', 'package-dart', 'package-php', 'package-locales', 'package-media', 'package-umd-bundle', 'package-json', 'package-readme', 'package-dts' )); // The release task prepares Blockly for an npm release. // It rebuilds the Blockly compressed files and updates the TypeScript // typings, and then packages all the npm release files into the /dist directory gulp.task('release', gulp.series(['build', 'typings', function(cb) { // Clean directory if exists if (fs.existsSync(packageDistribution)) { rimraf.sync(packageDistribution); } fs.mkdirSync(packageDistribution); cb(); }, 'package'])); // The default task builds Blockly. gulp.task('default', gulp.series(['build'])); // Stash current state, check out the named branch, and sync with // google/blockly. function syncBranch(branchName) { return function(done) { execSync('git stash save -m "Stash for sync"', { stdio: 'inherit' }); execSync('git checkout ' + branchName, { stdio: 'inherit' }); execSync('git pull ' + upstream_url + ' ' + branchName, { stdio: 'inherit' }); execSync('git push origin ' + branchName, { stdio: 'inherit' }); done(); } } // Stash current state, check out develop, and sync with google/blockly. gulp.task('git-sync-develop', syncBranch('develop')); // Stash current state, check out master, and sync with google/blockly. gulp.task('git-sync-master', syncBranch('master')); // Helper function: get a name for a rebuild branch. Format: rebuild_mm_dd_yyyy. function getRebuildBranchName() { var date = new Date(); var mm = date.getMonth() + 1; // Month, 0-11 var dd = date.getDate(); // Day of the month, 1-31 var yyyy = date.getFullYear(); return 'rebuild_' + mm + '_' + dd + '_' + yyyy; }; // Helper function: get a name for a rebuild branch. Format: rebuild_yyyy_mm. function getRCBranchName() { var date = new Date(); var mm = date.getMonth() + 1; // Month, 0-11 var yyyy = date.getFullYear(); return 'rc_' + yyyy + '_' + mm; }; // Recompile and push to origin. gulp.task('recompile', gulp.series([ 'git-sync-develop', function(done) { var branchName = getRebuildBranchName(); console.log('make-rebuild-branch: creating branch ' + branchName); execSync('git checkout -b ' + branchName, { stdio: 'inherit' }); done(); }, 'build', 'typings', function(done) { console.log('push-rebuild-branch: committing rebuild'); execSync('git commit -am "Rebuild"', { stdio: 'inherit' }); var branchName = getRebuildBranchName(); execSync('git push origin ' + branchName, { stdio: 'inherit' }); console.log('Branch ' + branchName + ' pushed to GitHub.'); console.log('Next step: create a pull request against develop.'); done(); } ]) ); // Create and push an RC branch. // Note that this pushes to google/blockly. gulp.task('git-create-rc', gulp.series([ 'git-sync-develop', function(done) { var branchName = getRCBranchName(); execSync('git checkout -b ' + branchName, { stdio: 'inherit' }); execSync('git push ' + upstream_url + ' ' + branchName, { stdio: 'inherit' }); done(); }, ]) ); // See https://docs.npmjs.com/cli/version. gulp.task('preversion', gulp.series([ 'git-sync-master', function(done) { // Create a branch named bump_version for the bump and rebuild. execSync('git checkout -b bump_version', { stdio: 'inherit' }); done(); }, ]) ); // See https://docs.npmjs.com/cli/version gulp.task('postversion', gulp.series([ function(done) { // Push both the branch and tag to google/blockly. execSync('git push ' + upstream_url + ' bump_version', { stdio: 'inherit' }); var tagName = 'v' + packageJson.version; execSync('git push ' + upstream_url + ' ' + tagName, { stdio: 'inherit' }); done(); } ]) );