Skip to content

Commit e2ae80a

Browse files
committed
feat(compiler): add ReplexicaCompiler
1 parent 25c986b commit e2ae80a

4 files changed

Lines changed: 123 additions & 0 deletions

File tree

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import * as t from "@babel/types";
2+
import { traverse } from '@babel/core';
3+
import { generateCodeFromBabelAst, parseCodeIntoBabelAst } from "../utils/babel";
4+
import { generateFileId } from "../utils/id";
5+
import { GeneratorResult } from "@babel/generator";
6+
import { hasDirective } from "../utils/ast";
7+
import { ReplexicaFileData, ReplexicaCompilerData } from "./types";
8+
import { ReplexicaScopeExtractor } from "./scope";
9+
10+
export class ReplexicaCompiler {
11+
public static fromCode(code: string, relativeFilePath: string, i18nImportPrefix: string) {
12+
const ast = parseCodeIntoBabelAst(code);
13+
const isServer = !hasDirective(ast, 'use client');
14+
15+
return new ReplexicaCompiler(relativeFilePath, code, ast, isServer, i18nImportPrefix);
16+
}
17+
18+
private constructor(
19+
public readonly relativeFilePath: string,
20+
private readonly code: string,
21+
public readonly ast: t.File,
22+
private readonly isServer: boolean,
23+
private readonly i18nImportPrefix: string,
24+
) {
25+
this.fileId = generateFileId(relativeFilePath, 0);
26+
}
27+
28+
private readonly fileId: string;
29+
30+
private _extractors: Set<ReplexicaScopeExtractor> = new Set();
31+
private _data: ReplexicaFileData = {};
32+
33+
public withScope(extractor: ReplexicaScopeExtractor): this {
34+
this._extractors.add(extractor);
35+
return this;
36+
}
37+
38+
public injectIntl(): this {
39+
const _compiler = this;
40+
traverse(this.ast, {
41+
enter(path) {
42+
try {
43+
for (const extractor of _compiler._extractors) {
44+
const scopes = extractor.fromNode(path);
45+
for (const scope of scopes) {
46+
const hints = scope.extractHints();
47+
const data = scope.injectIntl(_compiler.fileId, _compiler.isServer, _compiler.i18nImportPrefix);
48+
_compiler._data[scope.id] = { hints, data };
49+
}
50+
}
51+
} catch (thrownObject) {
52+
if (thrownObject === 'skip') {
53+
path.skip();
54+
} else {
55+
throw thrownObject;
56+
}
57+
}
58+
}
59+
});
60+
return this;
61+
}
62+
63+
public get data(): ReplexicaCompilerData {
64+
return {
65+
[this.fileId]: {
66+
context: { isClient: !this.isServer },
67+
data: this._data,
68+
},
69+
};
70+
}
71+
72+
public generate(): GeneratorResult {
73+
return generateCodeFromBabelAst(this.code, this.ast);
74+
}
75+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from './compiler';
2+
export * from './types';
3+
export * from './scope';
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './skip';
2+
export * from './types';
3+
export * from './content';
4+
export * from './attribute';
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
export type ReplexicaCompilerPayload = {
2+
settings: {
3+
locale: { source: string; };
4+
};
5+
data: ReplexicaCompilerData;
6+
};
7+
8+
export type ReplexicaCompilerData = {
9+
[fileId: string]: ReplexicaFilePayload;
10+
};
11+
12+
export type ReplexicaFileContext = {
13+
isClient: boolean;
14+
};
15+
16+
export type ReplexicaFilePayload = {
17+
context: ReplexicaFileContext;
18+
data: ReplexicaFileData;
19+
};
20+
21+
export type ReplexicaFileData = {
22+
[scopeId: string]: ReplexicaScopePayload;
23+
}
24+
25+
export type ReplexicaScopePayload = {
26+
hints: ReplexicaScopeHint[];
27+
data: ReplexicaScopeData;
28+
};
29+
30+
export type ReplexicaScopeHint = {
31+
type: 'attribute';
32+
name: string;
33+
} | {
34+
type: 'element';
35+
name: string;
36+
hint: string | null;
37+
};
38+
39+
export type ReplexicaScopeData = {
40+
[chunkId: string]: string;
41+
};

0 commit comments

Comments
 (0)