2021-03-11 09:56:49 +00:00
|
|
|
import { compile, readSchema } from '../index';
|
2021-03-09 18:29:31 +00:00
|
|
|
import fs from 'fs';
|
2021-03-11 09:56:49 +00:00
|
|
|
import path from 'path';
|
|
|
|
import { glob } from 'glob';
|
|
|
|
import yargs from 'yargs/yargs';
|
|
|
|
import * as M from '../meta';
|
|
|
|
|
|
|
|
export type CommandLineArguments = {
|
|
|
|
input: string;
|
|
|
|
base: string | undefined;
|
|
|
|
output: string | undefined;
|
|
|
|
core: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
export function computeBase(paths: string[]): string {
|
|
|
|
if (paths.length === 0) {
|
|
|
|
return '';
|
|
|
|
} else if (paths.length === 1) {
|
|
|
|
return path.dirname(paths[0]) + '/';
|
|
|
|
} else {
|
|
|
|
let i = 0;
|
|
|
|
while (true) {
|
|
|
|
let ch: string | null = null
|
|
|
|
for (const p of paths) {
|
|
|
|
if (i >= p.length) return p.slice(0, i);
|
|
|
|
if (ch === null) ch = p[i];
|
|
|
|
if (p[i] !== ch) return p.slice(0, i - 1);
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type ToCompile = {
|
|
|
|
inputFilePath: string,
|
|
|
|
outputFilePath: string,
|
|
|
|
schemaPath: M.ModulePath,
|
|
|
|
schema: M.Schema,
|
|
|
|
};
|
|
|
|
|
|
|
|
function changeExt(p: string, newext: string) {
|
|
|
|
return p.slice(0, -path.extname(p).length) + newext;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function run(options: CommandLineArguments) {
|
|
|
|
glob(options.input, (err, matches) => {
|
|
|
|
if (err) throw err;
|
|
|
|
|
|
|
|
const base = options.base ?? computeBase(matches);
|
|
|
|
const output = options.output ?? base;
|
|
|
|
|
|
|
|
const toCompile: Array<ToCompile> = matches.map(inputFilePath => {
|
|
|
|
if (!inputFilePath.startsWith(base)) {
|
|
|
|
throw new Error(`Input filename ${inputFilePath} falls outside base ${base}`);
|
|
|
|
}
|
|
|
|
const relPath = inputFilePath.slice(base.length);
|
|
|
|
const outputFilePath = path.join(output, changeExt(relPath, '.ts'));
|
|
|
|
const src = fs.readFileSync(inputFilePath, 'utf-8');
|
|
|
|
const schema = readSchema(src);
|
|
|
|
const schemaPath = relPath.split(path.delimiter).map(p => p.split('.')[0]).map(Symbol.for);
|
|
|
|
return { inputFilePath, outputFilePath, schemaPath, schema };
|
|
|
|
});
|
|
|
|
|
|
|
|
toCompile.forEach(c => {
|
|
|
|
const env: M.Environment = toCompile.map(cc => ({
|
|
|
|
schema: cc.schema,
|
|
|
|
schemaModulePath: cc.schemaPath,
|
|
|
|
typescriptModulePath: path.relative(c.outputFilePath, cc.outputFilePath) || null,
|
|
|
|
}));
|
|
|
|
fs.mkdirSync(path.dirname(c.outputFilePath), { recursive: true });
|
|
|
|
fs.writeFileSync(c.outputFilePath, compile(env, c.schema, options.core), 'utf-8');
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2021-03-09 18:29:31 +00:00
|
|
|
|
|
|
|
export function main(argv: Array<string>) {
|
2021-03-11 09:56:49 +00:00
|
|
|
const options: CommandLineArguments = yargs(argv)
|
|
|
|
.command('$0 <input>',
|
|
|
|
'Compile Preserves schema definitions to TypeScript',
|
|
|
|
yargs => yargs
|
|
|
|
.positional('input', {
|
|
|
|
type: 'string',
|
|
|
|
description: 'Input filename or glob',
|
|
|
|
demandOption: true,
|
|
|
|
})
|
|
|
|
.option('output', {
|
|
|
|
type: 'string',
|
|
|
|
description: 'Output directory for sources (default: next to sources)',
|
|
|
|
})
|
|
|
|
.option('base', {
|
|
|
|
type: 'string',
|
|
|
|
description: 'Base directory for sources (default: common prefix)',
|
|
|
|
})
|
|
|
|
.option('core', {
|
|
|
|
type: 'string',
|
|
|
|
description: 'Import path for @preserves/core',
|
|
|
|
default: '@preserves/core',
|
|
|
|
}),
|
|
|
|
argv => argv)
|
|
|
|
.argv;
|
|
|
|
run(options);
|
2021-03-09 18:29:31 +00:00
|
|
|
}
|