threeify-glsl-transpiler
Version:
A glsl to JS module transpiler.
229 lines (201 loc) • 6.55 kB
text/typescript
/* eslint-disable no-console */
':'; //# comment; exec /usr/bin/env node --experimental-modules "$0" "$@"
import { program } from 'commander';
import fs from 'fs';
import glob from 'glob';
import path from 'path';
import process, { exit } from 'process';
import watch from 'watch';
import { Options } from './Options.js';
import { glslToJavaScriptTranspiler } from './transpiler.js';
function commaSeparatedList(value: string): string[] {
return value.split(',');
}
const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8'));
program
.name('threeify-glsl-transpiler')
.version(packageJson.version)
.option(
'-p, --projectDir <dirpath>',
`the root of the project directory tree`
)
.option('-r, --rootDir <dirpath>', `the root of the source directory tree`)
.option(
'-i',
'--includeDirs <dirpaths>',
'a series of comma separated include directories'
)
.option('-o, --outDir <dirpath>', `the root of the output directory tree`)
.option('-w, --watch', `watch and incremental transpile any changed files`)
.option(
'-j, --allowJSIncludes',
`allow referencing javascript and typescript code via includes`
)
.option('-m, --minify', `reduce the size of the glsl code`)
.option(
'-e, --extensions <items>',
'comma separated list of extensions to transpile',
commaSeparatedList
)
.option(
'-v, --verboseLevel <level>',
`higher numbers means more output`,
parseInt
);
program.parse(process.argv);
/*
function removeUndefined(obj:any): any {
const ret = {};
Object.keys(obj)
.filter((key) => obj[key] !== undefined)
.forEach((key) => (ret[key] = obj[key]));
return ret;
}*/
const options = new Options();
//console.log('fresh options');
//console.log(options);
let projectDir = process.cwd();
const programOptions = program.opts();
if (programOptions.projectDir) {
projectDir = programOptions.projectDir;
}
const tsConfigFilePath = path.join(projectDir, 'tsconfig.json');
if (fs.existsSync(tsConfigFilePath)) {
const tsConfig = JSON.parse(fs.readFileSync(tsConfigFilePath, 'utf8'));
if (tsConfig.compilerOptions) {
if (options.verboseLevel >= 1) {
console.log(` inferring setup from ${tsConfigFilePath}.`);
}
options.safeCopy({
rootDir: tsConfig.compilerOptions.rootDir,
outDir: tsConfig.compilerOptions.outDir
});
//console.log('merge in tsconfig');
//console.log(options);
}
}
const threeifyFilePath = path.join(projectDir, 'threeify.json');
if (fs.existsSync(threeifyFilePath)) {
const threeifyConfig = JSON.parse(fs.readFileSync(threeifyFilePath, 'utf8'));
if (options.verboseLevel >= 1) {
console.log(` reading settings from ${threeifyFilePath}.`);
}
if (threeifyConfig.glsl) {
options.safeCopy(threeifyConfig.glsl);
//console.log('merge in threeify config');
//console.log(options);
}
}
if (options.verboseLevel >= 1) {
console.log(` applying command line overrides.`);
}
options.safeCopy(program);
//console.log('merge in cmd line');
//console.log(options);
if (options.verboseLevel >= 2) {
console.log(options);
}
options.extensions = options.extensions.map((ext) => ext.toLowerCase());
if (!options.rootDir) {
console.error(`no rootDir specified`);
exit(0);
}
if (!fs.existsSync(options.rootDir)) {
console.error(`rootDir doesn't exist: ${options.rootDir}`);
exit(0);
}
if (!options.outDir) {
console.error(`no outDir specified`);
exit(0);
}
options.rootDir = path.normalize(path.join(projectDir, options.rootDir));
options.outDir = path.normalize(path.join(projectDir, options.outDir));
options.includeDirs = options.includeDirs.map((includeDir) =>
path.normalize(path.join(projectDir, includeDir))
);
if (options.verboseLevel >= 2) {
console.log(options);
}
let numFiles = 0;
let numErrors = 0;
function inputFileNameToOutputFileName(inputFileName: string): string {
inputFileName = path.normalize(inputFileName);
const outputFileName =
inputFileName.replace(options.rootDir, options.outDir) + '.js';
return outputFileName;
}
function transpile(sourceFileName: string): string[] {
if (!fs.lstatSync(sourceFileName).isFile()) {
return [];
}
sourceFileName = path.normalize(sourceFileName);
const outputFileName = inputFileNameToOutputFileName(sourceFileName);
const fileErrors = glslToJavaScriptTranspiler(
sourceFileName,
outputFileName,
options
);
if (fileErrors.length > 0) {
numErrors++;
console.error(
` ${sourceFileName} --> ${path.basename(outputFileName)}: ${
fileErrors.length
} Errors.`
);
fileErrors.forEach((error) => {
console.error(` ${error}`);
});
} else {
if (options.verboseLevel >= 1) {
console.log(` ${sourceFileName} --> ${path.basename(outputFileName)}`);
}
}
return fileErrors;
}
function isFileSupported(fileName: string): boolean {
let ext = path.extname(fileName);
if (ext.length > 1) {
ext = ext.slice(1);
}
const result = options.extensions.includes(ext.toLowerCase());
return result;
}
// options is optional
const extGlob = options.extensions.join('|');
const globRegex = `${options.rootDir}/**/*.+(${extGlob})`;
glob(globRegex, {}, function (er, sourceFileNames) {
sourceFileNames.forEach((inputFileName) => {
numFiles++;
transpile(inputFileName);
});
if (numErrors > 0) {
console.error(`${numErrors} files failed to transpile.`);
}
console.log(`${numFiles - numErrors} files transpile successfully.`);
if (programOptions.watch) {
watch.createMonitor(options.rootDir, function (monitor) {
monitor.on('created', function (sourceFileName: string, stat) {
if (options.verboseLevel > 1) console.log(`created ${sourceFileName}`);
if (isFileSupported(sourceFileName)) {
transpile(sourceFileName);
}
});
monitor.on('changed', function (sourceFileName: string, curr, prev) {
if (options.verboseLevel > 1) console.log(`changed ${sourceFileName}`);
if (isFileSupported(sourceFileName)) {
transpile(sourceFileName);
}
});
monitor.on('removed', function (sourceFileName: string, stat) {
if (options.verboseLevel > 1) console.log(`removed ${sourceFileName}`);
if (isFileSupported(sourceFileName)) {
const outputFileName = inputFileNameToOutputFileName(sourceFileName);
if (fs.existsSync(outputFileName)) {
fs.unlinkSync(outputFileName);
}
}
});
});
}
});