Skip to content

Instantly share code, notes, and snippets.

@jonschlinkert
Created June 20, 2022 07:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonschlinkert/55a3f68ad5eb77c4a0b77e24a275f04a to your computer and use it in GitHub Desktop.
Save jonschlinkert/55a3f68ad5eb77c4a0b77e24a275f04a to your computer and use it in GitHub Desktop.
const NEWLINE_REGEX = /\r?\n/;
const INCLUDE_REGEX = /^(\s*)include\(([A-Z][0-9a-zA-Z]*?)\)/;
const SECTION_START_REGEX = /^([a-z]+) ([A-Z][0-9a-zA-Z]*?)\s*{/;
const SECTION_END_REGEX = /(?<=})/;
class Section {
type = '';
name = '';
leading = [];
body = [];
expand(fragments = {}) {
const body = [this.leading];
for (let i = 0; i < this.body.length; i++) {
const line = this.body[i];
const match = INCLUDE_REGEX.exec(line);
if (!match) {
body.push(line);
continue;
}
if (!fragments[match[2]]) {
throw new SyntaxError('Invalid fragment name: ' + match[2]);
}
body.push(...fragments[match[2]].map(f => match[1] + f.trim()));
}
return body.join('\n');
}
stringify() {
return [this.leading, ...this.body].join('\n');
}
}
class PreProcessor {
constructor(source = '', fragments) {
this.source = source;
this.sections = [];
this.fragments = { ...fragments };
}
tokenize() {
for (const chunk of this.source.split(SECTION_END_REGEX)) {
const lines = chunk.split(NEWLINE_REGEX);
const section = new Section();
let prev;
while (lines.length) {
const match = SECTION_START_REGEX.exec(lines[0]);
if (match) {
section.type = match[1];
section.name = match[2];
section.body = lines;
this.sections.push(section);
if (section.type === 'fragment' && !this.fragments[section.name]) {
this.fragments[section.name] = section.body.slice(1, -1);
}
break;
} else if (!(prev === '' && lines[0] === '')) {
section.leading.push(lines[0]);
}
prev = lines[0];
section.body.push(lines.shift());
}
section.leading = section.leading.join('\n');
}
return this.sections;
}
stringify() {
return this.sections.map(s => s.stringify()).join('\n');
}
expand(fragments = this.fragments) {
if (!this.sections.length) this.tokenize();
return this.sections.map(s => s.expand(fragments)).join('\n');
}
}
module.exports = PreProcessor;
const string = `
fragment Basics {
updatedAt DateTime @updatedAt
createdAt DateTime @default(now())
}
//
// This
// is a
// comment
//
model User {
include(Id)
include(Basics)
}
model Task {
include(Id)
include(Basics)
}
`;
const preprocessor = new PreProcessor(string, {
Id: ['id String @id @default(cuid())']
});
// const sections = preprocessor.tokenize();
// console.log(preprocessor);
// console.log(preprocessor.stringify());
const expanded = preprocessor.expand();
console.log(expanded);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment