summaryrefslogtreecommitdiffstats
path: root/src/arrow/js/gulp/closure-task.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/arrow/js/gulp/closure-task.js')
-rw-r--r--src/arrow/js/gulp/closure-task.js213
1 files changed, 213 insertions, 0 deletions
diff --git a/src/arrow/js/gulp/closure-task.js b/src/arrow/js/gulp/closure-task.js
new file mode 100644
index 000000000..6e5a61d82
--- /dev/null
+++ b/src/arrow/js/gulp/closure-task.js
@@ -0,0 +1,213 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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 {
+ targetDir,
+ mainExport,
+ esmRequire,
+ gCCLanguageNames,
+ publicModulePaths,
+ observableFromStreams,
+ shouldRunInChildProcess,
+ spawnGulpCommandInChildProcess,
+} = require('./util');
+
+const fs = require('fs');
+const gulp = require('gulp');
+const path = require('path');
+const mkdirp = require('mkdirp');
+const sourcemaps = require('gulp-sourcemaps');
+const { memoizeTask } = require('./memoize-task');
+const { compileBinFiles } = require('./typescript-task');
+const closureCompiler = require('google-closure-compiler').gulp();
+
+const closureTask = ((cache) => memoizeTask(cache, async function closure(target, format) {
+
+ if (shouldRunInChildProcess(target, format)) {
+ return spawnGulpCommandInChildProcess('compile', target, format);
+ }
+
+ const src = targetDir(target, `cls`);
+ const srcAbsolute = path.resolve(src);
+ const out = targetDir(target, format);
+ const externs = path.join(`${out}/${mainExport}.externs.js`);
+ const entry_point = path.join(`${src}/${mainExport}.dom.cls.js`);
+
+ const exportedImports = publicModulePaths(srcAbsolute).reduce((entries, publicModulePath) => [
+ ...entries, {
+ publicModulePath,
+ exports_: getPublicExportedNames(esmRequire(publicModulePath))
+ }
+ ], []);
+
+ await mkdirp(out);
+
+ await Promise.all([
+ fs.promises.writeFile(externs, generateExternsFile(exportedImports)),
+ fs.promises.writeFile(entry_point, generateUMDExportAssignment(srcAbsolute, exportedImports))
+ ]);
+
+ return await Promise.all([
+ runClosureCompileAsObservable().toPromise(),
+ compileBinFiles(target, format).toPromise()
+ ]);
+
+ function runClosureCompileAsObservable() {
+ return observableFromStreams(
+ gulp.src([
+ /* external libs first */
+ `node_modules/flatbuffers/package.json`,
+ `node_modules/flatbuffers/js/flatbuffers.mjs`,
+ `${src}/**/*.js` /* <-- then source globs */
+ ], { base: `./` }),
+ sourcemaps.init(),
+ closureCompiler(createClosureArgs(entry_point, externs, target), {
+ platform: ['native', 'java', 'javascript']
+ }),
+ // rename the sourcemaps from *.js.map files to *.min.js.map
+ sourcemaps.write(`.`, { mapFile: (mapPath) => mapPath.replace(`.js.map`, `.${target}.min.js.map`) }),
+ gulp.dest(out)
+ );
+ }
+}))({});
+
+module.exports = closureTask;
+module.exports.closureTask = closureTask;
+
+const createClosureArgs = (entry_point, externs, target) => ({
+ externs,
+ entry_point,
+ third_party: true,
+ warning_level: `QUIET`,
+ dependency_mode: `PRUNE`,
+ rewrite_polyfills: false,
+ module_resolution: `NODE`,
+ // formatting: `PRETTY_PRINT`,
+ // debug: true,
+ compilation_level: `ADVANCED`,
+ package_json_entry_names: `module,jsnext:main,main`,
+ assume_function_wrapper: true,
+ js_output_file: `${mainExport}.js`,
+ language_in: gCCLanguageNames[`esnext`],
+ language_out: gCCLanguageNames[target],
+ output_wrapper:`${apacheHeader()}
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (factory(global.Arrow = global.Arrow || {}));
+}(this, (function (exports) {%output%}.bind(this))));`
+});
+
+function generateUMDExportAssignment(src, exportedImports) {
+ return [
+ ...exportedImports.map(({ publicModulePath }, i) => {
+ const p = publicModulePath.slice(src.length + 1);
+ return (`import * as exports${i} from './${p}';`);
+ }).filter(Boolean),
+ 'Object.assign(arguments[0], exports0);'
+ ].join('\n');
+}
+
+function generateExternsFile(exportedImports) {
+ return [
+ externsHeader(),
+ ...exportedImports.reduce((externBodies, { exports_ }) => [
+ ...externBodies, ...exports_.map(externBody)
+ ], []).filter(Boolean)
+ ].join('\n');
+}
+
+function externBody({ exportName, staticNames, instanceNames }) {
+ return [
+ `var ${exportName} = function() {};`,
+ staticNames.map((staticName) => (isNaN(+staticName)
+ ? `/** @type {?} */\n${exportName}.${staticName} = function() {};`
+ : `/** @type {?} */\n${exportName}[${staticName}] = function() {};`
+ )).join('\n'),
+ instanceNames.map((instanceName) => (isNaN(+instanceName)
+ ? `/** @type {?} */\n${exportName}.prototype.${instanceName};`
+ : `/** @type {?} */\n${exportName}.prototype[${instanceName}];`
+ )).join('\n')
+ ].filter(Boolean).join('\n');
+}
+
+function externsHeader() {
+ return (`${apacheHeader()}
+// @ts-nocheck
+/* eslint-disable */
+/**
+ * @fileoverview Closure Compiler externs for Arrow
+ * @externs
+ * @suppress {duplicate,checkTypes}
+ */
+/** @type {symbol} */
+Symbol.iterator;
+/** @type {symbol} */
+Symbol.toPrimitive;
+/** @type {symbol} */
+Symbol.asyncIterator;
+`);
+}
+
+function getPublicExportedNames(entryModule) {
+ const fn = function() {};
+ const isStaticOrProtoName = (x) => (
+ !(x in fn) &&
+ (x !== `default`) &&
+ (x !== `undefined`) &&
+ (x !== `__esModule`) &&
+ (x !== `constructor`) &&
+ !(x.startsWith('_'))
+ );
+ return Object
+ .getOwnPropertyNames(entryModule)
+ .filter((name) => name !== 'default')
+ .filter((name) => (
+ typeof entryModule[name] === `object` ||
+ typeof entryModule[name] === `function`
+ ))
+ .map((name) => [name, entryModule[name]])
+ .reduce((reserved, [name, value]) => {
+
+ const staticNames = value &&
+ typeof value === 'object' ? Object.getOwnPropertyNames(value).filter(isStaticOrProtoName) :
+ typeof value === 'function' ? Object.getOwnPropertyNames(value).filter(isStaticOrProtoName) : [];
+
+ const instanceNames = (typeof value === `function` && Object.getOwnPropertyNames(value.prototype || {}) || []).filter(isStaticOrProtoName);
+
+ return [...reserved, { exportName: name, staticNames, instanceNames }];
+ }, []);
+}
+
+function apacheHeader() {
+ return `// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you 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.`;
+}