Skip to content

Commit 392bb1c

Browse files
feat: add conditional rsc mode support (#21)
* feat: add conditional `rsc` mode support * chore: add changeset * refactor(compiler): restructure plugins dir
1 parent f893048 commit 392bb1c

File tree

11 files changed

+102
-83
lines changed

11 files changed

+102
-83
lines changed

.changeset/seven-poems-retire.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@replexica/compiler": minor
3+
---
4+
5+
The compiler now supports the rsc flag, which is set to true by default. When set to false, every piece of code is processed as if it had 'use client'.

packages/compiler/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
"object-hash": "^3.0.0",
2727
"typescript": "^5.4.3",
2828
"unplugin": "^1.10.0",
29-
"vitest": "^1.4.0"
29+
"vitest": "^1.4.0",
30+
"zod": "^3.22.4"
3031
},
3132
"devDependencies": {
3233
"@types/babel__core": "^7.20.5",

packages/compiler/src/compiler/compiler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import { ReplexicaFileData, ReplexicaCompilerData } from "./types";
88
import { ReplexicaScopeExtractor } from "./scope";
99

1010
export class ReplexicaCompiler {
11-
public static fromCode(code: string, relativeFilePath: string) {
11+
public static fromCode(code: string, relativeFilePath: string, rsc: boolean) {
1212
const ast = parseCodeIntoBabelAst(code);
13-
const isServer = !hasDirective(ast, 'use client');
13+
const isServer = !rsc ? false : !hasDirective(ast, 'use client');
1414

1515
return new ReplexicaCompiler(relativeFilePath, code, ast, isServer);
1616
}

packages/compiler/src/index.ts

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,5 @@
1-
import { createUnplugin } from "unplugin"
2-
import { ReplexicaAttributeScope, ReplexicaCompiler, ReplexicaContentScope, ReplexicaSkipScope } from "./compiler";
3-
import { ReplexicaOutputProcessor } from "./output";
4-
import path from 'path';
5-
import { createNextPlugin } from "./plugins";
6-
import { ReplexicaConfig } from "./types";
1+
import { nextPlugin } from "./plugins/next";
72

8-
const unplugin = createUnplugin<ReplexicaConfig>((options) => ({
9-
name: '@replexica/compiler',
10-
enforce: 'pre',
11-
transformInclude(id) {
12-
// .tsx and .jsx files
13-
return /\.(t|j)sx$/.test(id);
14-
},
15-
transform(code, absoluteFilePath) {
16-
try {
17-
const relativeFilePath = path.relative(process.cwd(), absoluteFilePath);
18-
19-
const compiler = ReplexicaCompiler
20-
.fromCode(code, relativeFilePath)
21-
.withScope(ReplexicaSkipScope)
22-
.withScope(ReplexicaAttributeScope)
23-
.withScope(ReplexicaContentScope)
24-
.injectIntl();
25-
26-
const result = compiler.generate();
27-
28-
const outputProcessor = ReplexicaOutputProcessor.create(relativeFilePath, options);
29-
outputProcessor.saveBuildData(compiler.data);
30-
outputProcessor.saveFullSourceLocaleData(compiler.data);
31-
outputProcessor.saveClientSourceLocaleData(compiler.data);
32-
outputProcessor.saveStubLocaleData();
33-
34-
if (options.debug) {
35-
outputProcessor.saveAst(compiler.ast);
36-
outputProcessor.saveOutput(result.code);
37-
}
38-
39-
return {
40-
code: result.code,
41-
map: result.map,
42-
};
43-
} catch (error: any) {
44-
throw new ReplexicaError(error.message);
45-
}
46-
},
47-
}));
48-
49-
class ReplexicaError extends Error {
50-
51-
}
52-
export * from './types';
533
export default {
54-
next: createNextPlugin(unplugin),
4+
next: nextPlugin,
555
};

packages/compiler/src/options.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Z from 'zod';
2+
3+
const supportedLocale = Z.enum(['en', 'es']);
4+
const optionsSchema = Z.object({
5+
locale: Z.object({
6+
source: supportedLocale,
7+
targets: Z.array(supportedLocale),
8+
}),
9+
rsc: Z.boolean().optional().default(true),
10+
debug: Z.boolean().optional().default(false),
11+
});
12+
13+
export type ReplexicaConfig = Z.infer<typeof optionsSchema>;
14+
15+
export function parseOptions(options: unknown): ReplexicaConfig {
16+
return optionsSchema.parse(options);
17+
}

packages/compiler/src/output.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { File } from "@babel/types";
22
import fs from 'fs';
33
import path from 'path';
4-
import { ReplexicaCompilerData, ReplexicaCompilerPayload } from "./compiler";
5-
import { ReplexicaLocaleData, ReplexicaConfig, ReplexicaData } from "./types";
4+
import { ReplexicaCompilerData } from "./compiler";
5+
import { ReplexicaLocaleData, ReplexicaData } from "./types";
6+
import { ReplexicaConfig } from "./options";
67

78
export class ReplexicaOutputProcessor {
89
public static create(relativeFilePath: string, options: ReplexicaConfig) {

packages/compiler/src/plugins.ts

Lines changed: 0 additions & 18 deletions
This file was deleted.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { createUnplugin } from "unplugin"
2+
import { ReplexicaAttributeScope, ReplexicaCompiler, ReplexicaContentScope, ReplexicaSkipScope } from "./../compiler";
3+
import { ReplexicaOutputProcessor } from "./../output";
4+
import path from 'path';
5+
import { ReplexicaConfig, parseOptions } from "../options";
6+
7+
export const basePlugin = createUnplugin<Partial<ReplexicaConfig>>((_options) => ({
8+
name: '@replexica/compiler',
9+
enforce: 'pre',
10+
transformInclude(id) {
11+
// .tsx and .jsx files
12+
return /\.(t|j)sx$/.test(id);
13+
},
14+
transform(code, absoluteFilePath) {
15+
const options = parseOptions(_options);
16+
try {
17+
const relativeFilePath = path.relative(process.cwd(), absoluteFilePath);
18+
19+
const compiler = ReplexicaCompiler
20+
.fromCode(code, relativeFilePath, options.rsc)
21+
.withScope(ReplexicaSkipScope)
22+
.withScope(ReplexicaAttributeScope)
23+
.withScope(ReplexicaContentScope)
24+
.injectIntl();
25+
26+
const result = compiler.generate();
27+
28+
const outputProcessor = ReplexicaOutputProcessor.create(relativeFilePath, options);
29+
outputProcessor.saveBuildData(compiler.data);
30+
outputProcessor.saveFullSourceLocaleData(compiler.data);
31+
outputProcessor.saveClientSourceLocaleData(compiler.data);
32+
outputProcessor.saveStubLocaleData();
33+
34+
if (options.debug) {
35+
outputProcessor.saveAst(compiler.ast);
36+
outputProcessor.saveOutput(result.code);
37+
}
38+
39+
return {
40+
code: result.code,
41+
map: result.map,
42+
};
43+
} catch (error: any) {
44+
throw new ReplexicaError(error.message);
45+
}
46+
},
47+
}));
48+
49+
class ReplexicaError extends Error {
50+
51+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { basePlugin } from './base';
2+
import { ReplexicaConfig } from '../options';
3+
4+
export function nextPlugin<O extends ReplexicaConfig>(replexicaOptions: O, nextConfig: any) {
5+
return {
6+
...nextConfig,
7+
webpack(config: any, options: any) {
8+
config.plugins.unshift(basePlugin.webpack(replexicaOptions));
9+
10+
if (typeof nextConfig.webpack === 'function') {
11+
return nextConfig.webpack(config, options);
12+
}
13+
14+
return config;
15+
},
16+
};
17+
}

packages/compiler/src/types.ts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
import { ReplexicaScopeHint } from "./compiler";
22

3-
export type ReplexicaConfig = {
4-
locale: {
5-
source: string;
6-
targets: string[];
7-
};
8-
debug?: boolean;
9-
};
10-
113
export type ReplexicaLocaleData = {
124
[fileId: string]: {
135
[scopeId: string]: {

0 commit comments

Comments
 (0)