blob: 6916b59adf6f8233b9ba829779c0c1047bdc8cbb [file]
// 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.
import { targetDir, mainExport, gCCLanguageNames, publicModulePaths, observableFromStreams, shouldRunInChildProcess, spawnGulpCommandInChildProcess } from "./util.js";
import fs from 'node:fs';
import gulp from 'gulp';
import Path from 'node:path';
import https from 'node:https';
import { mkdirp } from 'mkdirp';
import { PassThrough } from 'node:stream';
import sourcemaps from 'gulp-sourcemaps';
import { memoizeTask } from './memoize-task.js';
import { compileBinFiles } from './typescript-task.js';
import { gulp as compiler } from 'google-closure-compiler';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const closureCompilerVer = JSON.parse(fs.readFileSync(Path.join(__dirname, '..', 'package.json'))).devDependencies['google-closure-compiler'].split('.')[0];
export 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 = [];
for (const publicModulePath of publicModulePaths(srcAbsolute)) {
exportedImports.push({
publicModulePath,
exports_: getPublicExportedNames(await import(`file://${publicModulePath}`))
});
}
await mkdirp(out);
await Promise.all([
fs.promises.writeFile(externs, generateExternsFile(exportedImports)),
fs.promises.writeFile(entry_point, generateUMDExportAssignment(srcAbsolute, exportedImports))
]);
const closeCompilerPolyfills = [];
await Promise.all([
runClosureCompileAsObservable().toPromise(),
compileBinFiles(target, format).toPromise().then(() => Promise.all([
observableFromStreams(gulp.src(`${src}/**/*.d.ts`), gulp.dest(out)).toPromise(), // copy .d.ts files,
observableFromStreams(gulp.src(`${src}/**/*.d.ts.map`), gulp.dest(out)).toPromise(), // copy .d.ts.map files,
observableFromStreams(gulp.src(`${src}/src/**/*`), gulp.dest(`${out}/src`)).toPromise(), // copy TS source files,
]))
]);
// Download the closure compiler polyfill sources for sourcemaps
await Promise.all(closeCompilerPolyfills.map(async (path) => {
await fs.promises.mkdir(
Path.join(out, Path.parse(path).dir),
{ recursive: true, mode: 0o755 }
);
const res = new PassThrough();
const req = https.request(
new URL(`https://raw.githubusercontent.com/google/closure-compiler/v${closureCompilerVer}/${path}`),
(res_) => {
if (res_.statusCode === 200) {
res_.pipe(res);
} else {
res.end();
}
}
);
req.on('error', (e) => res.emit('error', e)).end();
return observableFromStreams(res, fs.createWriteStream(Path.join(out, path))).toPromise();
}));
function runClosureCompileAsObservable() {
return observableFromStreams(
gulp.src([
/* external libs first */
`node_modules/flatbuffers/package.json`,
`node_modules/flatbuffers/**/*.js`,
`${src}/**/*.js` /* <-- then source globs */
], { base: `./` }),
sourcemaps.init(),
compiler(createClosureArgs(entry_point, externs, target), {
platform: ['native', 'java', 'javascript']
}),
sourcemaps.mapSources((path) => {
if (path.indexOf(`${src}/`) === 0) {
return path.slice(`${src}/`.length);
}
if (path.includes('com/google')) {
closeCompilerPolyfills.push(path);
return path.slice(`src/`.length);
}
return path;
}),
// rename the sourcemaps from *.js.map files to *.min.js.map
sourcemaps.write(`./`, {
sourceRoot: './src',
includeContent: false,
mapFile: (mapPath) => mapPath.replace(`.js.map`, `.${target}.min.js.map`),
}),
gulp.dest(out)
);
}
}))({});
export default 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;
var Encoding = function() {};
/** @type {?} */
Encoding[1] = function() {};
/** @type {?} */
Encoding[2] = function() {};
/** @type {?} */
Encoding.UTF8_BYTES = function() {};
/** @type {?} */
Encoding.UTF16_STRING = function() {};
`);
}
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.`;
}