forked from PolymerLabs/arcs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
schema2base.ts
executable file
·122 lines (98 loc) · 3.67 KB
/
schema2base.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* @license
* Copyright (c) 2019 Google Inc. All rights reserved.
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
* Code distributed by Google as part of this project is also
* subject to an additional IP rights grant found at
* http://polymer.github.io/PATENTS.txt
*/
import fs from 'fs';
import path from 'path';
import minimist from 'minimist';
import {Manifest} from '../runtime/manifest.js';
import {Runtime} from '../runtime/runtime.js';
import {SchemaGraph, SchemaNode} from './schema2graph.js';
import {ParticleSpec} from '../runtime/arcs-types/particle-spec.js';
import {PATHS} from './paths.oss.js';
Runtime.init('../..', PATHS);
export interface EntityGenerator {
generate(): string;
}
export class NodeAndGenerator {
node: SchemaNode;
generator: EntityGenerator;
}
export abstract class Schema2Base {
namespace: string;
constructor(readonly opts: minimist.ParsedArgs) {}
async call() {
fs.mkdirSync(this.opts.outdir, {recursive: true});
for (const file of this.opts._) {
if (fs.existsSync(file)) {
await this.processFile(file);
} else {
throw new Error(`File not found: ${file}`);
}
}
}
private async processFile(src: string) {
const outName = this.opts.outfile || this.outputName(path.basename(src));
const outPath = path.join(this.opts.outdir, outName);
if (this.opts.update && fs.existsSync(outPath) && fs.statSync(outPath).mtimeMs > fs.statSync(src).mtimeMs) {
return;
}
const manifest = await Runtime.parseFile(src);
if (manifest.errors.some(e => e.severity !== 'warning')) {
return;
}
this.namespace = manifest.meta.namespace;
if (!this.namespace) {
throw new Error(`Namespace is required in '${src}' for code generation.`);
}
const classes = await this.processManifest(manifest);
if (classes.length === 0) {
console.warn(`Could not find any particle in '${src}'`);
}
const outFile = fs.openSync(outPath, 'w');
fs.writeSync(outFile, this.fileHeader(outName));
for (const text of classes) {
fs.writeSync(outFile, text.replace(/ +\n/g, '\n'));
}
fs.writeSync(outFile, this.fileFooter());
fs.closeSync(outFile);
}
async processManifest(manifest: Manifest): Promise<string[]> {
// TODO: consider an option to generate one file per particle
const classes: string[] = [];
for (const particle of manifest.particles) {
const nodes = await this.calculateNodeAndGenerators(particle);
classes.push(...nodes.map(ng => ng.generator.generate()));
if (this.opts.test_harness) {
classes.push(await this.generateTestHarness(particle, nodes.map(n => n.node)));
continue;
}
classes.push(await this.generateParticleClass(particle, nodes));
}
return classes;
}
async calculateNodeAndGenerators(particle: ParticleSpec): Promise<NodeAndGenerator[]> {
const graph = new SchemaGraph(particle);
const nodes: NodeAndGenerator[] = [];
for (const node of graph.walk()) {
const generator = this.getEntityGenerator(node);
await node.calculateHash();
nodes.push({node, generator});
}
return nodes;
}
upperFirst(s: string): string {
return s[0].toUpperCase() + s.slice(1);
}
outputName(baseName: string): string { return ''; }
fileHeader(outName: string): string { return ''; }
fileFooter(): string { return ''; }
abstract getEntityGenerator(node: SchemaNode): EntityGenerator;
abstract async generateParticleClass(particle: ParticleSpec, nodes: NodeAndGenerator[]): Promise<string>;
abstract async generateTestHarness(particle: ParticleSpec, nodes: SchemaNode[]): Promise<string>;
}