@farm.js/plugin 0.1.0-beta.103 → 0.1.0-beta.105
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server-fn-transform.d.ts","sourceRoot":"","sources":["../../src/rsc/server-fn-transform.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server-fn-transform.d.ts","sourceRoot":"","sources":["../../src/rsc/server-fn-transform.ts"],"names":[],"mappings":"AAkBA,MAAM,MAAM,2BAA2B,GAAG;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,MAAM,GACT,2BAA2B,GAAG,IAAI,CAoDpC"}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
const SERVER_FUNCTION_FACTORY_IMPORT_RE = /\bimport\s+(?:type\s+)?(?:[A-Za-z_$][\w$]*\s*,\s*)?(\{[^}]*\})\s+from\s*["']@farm.js\/core(?:\/(?:server-fn|server-query))?["']\s*;?/g;
|
|
2
2
|
const EXPORT_SERVER_FUNCTION_FACTORY_RE = /\bexport\s+(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([A-Za-z_$][\w$]*)\s*(?:<[^<>]*(?:<[^<>]*>[^<>]*)*>)?\s*\(/g;
|
|
3
3
|
const EXPORT_DEFAULT_SERVER_FUNCTION_FACTORY_RE = /\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*(?:<[^<>]*(?:<[^<>]*>[^<>]*)*>)?\s*\(/g;
|
|
4
|
+
// Any assignment counts as a declaration, not only `const x = factory(...)`:
|
|
5
|
+
// splitting `let save; save = createServerFn(...)` must not hide the binding
|
|
6
|
+
// from the fail-closed export checks below.
|
|
7
|
+
const SERVER_FUNCTION_DECLARATION_RE = /(?<![.\w$])([A-Za-z_$][\w$]*)\s*=\s*([A-Za-z_$][\w$]*)\s*(?:<[^<>]*(?:<[^<>]*>[^<>]*)*>)?\s*\(/g;
|
|
8
|
+
const LOCAL_EXPORT_CLAUSE_RE = /\bexport\s*\{([^}]*)\}/g;
|
|
9
|
+
const EXPORT_DEFAULT_ALIAS_RE = /\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*(?:;|(?=[\r\n])|$)/g;
|
|
10
|
+
const EXPORT_BINDING_ALIAS_RE = /\bexport\s+(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*([A-Za-z_$][\w$]*)\s*(?:;|(?=[,\r\n])|$)/g;
|
|
4
11
|
const MODULE_EXT_RE = /\.[cm]?[jt]sx?$/;
|
|
5
12
|
const DECLARATION_RE = /(^|\n)\s*["']([^"']+)["']\s*;?/g;
|
|
6
13
|
export function transformFarmServerFns(code, id) {
|
|
@@ -11,6 +18,20 @@ export function transformFarmServerFns(code, id) {
|
|
|
11
18
|
const factoryNames = findServerFunctionFactoryImportNames(code);
|
|
12
19
|
if (factoryNames.size === 0)
|
|
13
20
|
return null;
|
|
21
|
+
const reExport = findServerFnReExport(code, factoryNames);
|
|
22
|
+
if (reExport) {
|
|
23
|
+
const factoryName = reExport.factory === "fn" ? "createServerFn" : "createServerQuery";
|
|
24
|
+
const escape = reExport.form === "clause"
|
|
25
|
+
? "with an export clause"
|
|
26
|
+
: reExport.form === "default"
|
|
27
|
+
? "as the default export"
|
|
28
|
+
: "through another exported binding";
|
|
29
|
+
const inlineForm = reExport.form === "default"
|
|
30
|
+
? `export default ${factoryName}(...)`
|
|
31
|
+
: `export const ${reExport.exportName} = ${factoryName}(...)`;
|
|
32
|
+
throw new Error(`Server function "${reExport.localName}" cannot be re-exported ${escape}. ` +
|
|
33
|
+
`Export it inline instead: ${inlineForm}.`);
|
|
34
|
+
}
|
|
14
35
|
const replacements = findServerFnExportReplacements(code, factoryNames);
|
|
15
36
|
if (replacements.length === 0)
|
|
16
37
|
return null;
|
|
@@ -53,6 +74,135 @@ function findServerFunctionFactoryImportNames(code) {
|
|
|
53
74
|
}
|
|
54
75
|
return names;
|
|
55
76
|
}
|
|
77
|
+
function findServerFnReExport(code, factoryNames) {
|
|
78
|
+
// Comments and string contents must not hide or fake an export: a comment
|
|
79
|
+
// inside `export { /* … */ save }` previously made the specifier unparsable
|
|
80
|
+
// and the module shipped untransformed.
|
|
81
|
+
const scannable = stripCommentsAndStrings(code);
|
|
82
|
+
const declarations = new Map();
|
|
83
|
+
for (const match of scannable.matchAll(SERVER_FUNCTION_DECLARATION_RE)) {
|
|
84
|
+
const localName = match[1];
|
|
85
|
+
const factoryName = match[2];
|
|
86
|
+
const factory = factoryName ? factoryNames.get(factoryName) : undefined;
|
|
87
|
+
if (localName && factory)
|
|
88
|
+
declarations.set(localName, factory);
|
|
89
|
+
}
|
|
90
|
+
if (declarations.size === 0)
|
|
91
|
+
return null;
|
|
92
|
+
for (const match of scannable.matchAll(LOCAL_EXPORT_CLAUSE_RE)) {
|
|
93
|
+
const clause = match[1] ?? "";
|
|
94
|
+
const clauseEnd = (match.index ?? 0) + match[0].length;
|
|
95
|
+
if (scannable.slice(clauseEnd).trimStart().startsWith("from"))
|
|
96
|
+
continue;
|
|
97
|
+
for (const specifier of clause.split(",")) {
|
|
98
|
+
const parts = specifier.trim().split(/\s+as\s+/);
|
|
99
|
+
const localName = parts[0]?.trim();
|
|
100
|
+
if (!localName || !declarations.has(localName))
|
|
101
|
+
continue;
|
|
102
|
+
return {
|
|
103
|
+
localName,
|
|
104
|
+
exportName: parts[1]?.trim() || localName,
|
|
105
|
+
factory: declarations.get(localName),
|
|
106
|
+
form: "clause",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// `export default save;` — the export-default replacement only recognizes a
|
|
111
|
+
// direct factory call, so a bare identifier slipped through untransformed.
|
|
112
|
+
for (const match of scannable.matchAll(EXPORT_DEFAULT_ALIAS_RE)) {
|
|
113
|
+
const localName = match[1];
|
|
114
|
+
if (!localName || !declarations.has(localName))
|
|
115
|
+
continue;
|
|
116
|
+
return {
|
|
117
|
+
localName,
|
|
118
|
+
exportName: "default",
|
|
119
|
+
factory: declarations.get(localName),
|
|
120
|
+
form: "default",
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
// `export const persist = save;` — same escape through a renamed binding.
|
|
124
|
+
for (const match of scannable.matchAll(EXPORT_BINDING_ALIAS_RE)) {
|
|
125
|
+
const exportName = match[1];
|
|
126
|
+
const localName = match[2];
|
|
127
|
+
if (!exportName || !localName || !declarations.has(localName))
|
|
128
|
+
continue;
|
|
129
|
+
return {
|
|
130
|
+
localName,
|
|
131
|
+
exportName,
|
|
132
|
+
factory: declarations.get(localName),
|
|
133
|
+
form: "binding",
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Blank out comments and string/template contents, preserving length, so the
|
|
140
|
+
* export scans above see only real syntax. Quotes themselves are kept; the
|
|
141
|
+
* characters between them become spaces.
|
|
142
|
+
*/
|
|
143
|
+
function stripCommentsAndStrings(code) {
|
|
144
|
+
const output = code.split("");
|
|
145
|
+
let quote = null;
|
|
146
|
+
let lineComment = false;
|
|
147
|
+
let blockComment = false;
|
|
148
|
+
let escaped = false;
|
|
149
|
+
for (let index = 0; index < code.length; index++) {
|
|
150
|
+
const char = code[index];
|
|
151
|
+
const next = code[index + 1];
|
|
152
|
+
if (lineComment) {
|
|
153
|
+
if (char === "\n" || char === "\r")
|
|
154
|
+
lineComment = false;
|
|
155
|
+
else
|
|
156
|
+
output[index] = " ";
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (blockComment) {
|
|
160
|
+
if (char === "*" && next === "/") {
|
|
161
|
+
blockComment = false;
|
|
162
|
+
output[index] = " ";
|
|
163
|
+
output[index + 1] = " ";
|
|
164
|
+
index++;
|
|
165
|
+
}
|
|
166
|
+
else if (char !== "\n") {
|
|
167
|
+
output[index] = " ";
|
|
168
|
+
}
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (quote) {
|
|
172
|
+
if (escaped) {
|
|
173
|
+
escaped = false;
|
|
174
|
+
output[index] = " ";
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (char === "\\") {
|
|
178
|
+
escaped = true;
|
|
179
|
+
output[index] = " ";
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (char === quote) {
|
|
183
|
+
quote = null;
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (char !== "\n")
|
|
187
|
+
output[index] = " ";
|
|
188
|
+
continue;
|
|
189
|
+
}
|
|
190
|
+
if (char === "/" && next === "/") {
|
|
191
|
+
lineComment = true;
|
|
192
|
+
output[index] = " ";
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (char === "/" && next === "*") {
|
|
196
|
+
blockComment = true;
|
|
197
|
+
output[index] = " ";
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
if (char === "'" || char === '"' || char === "`") {
|
|
201
|
+
quote = char;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return output.join("");
|
|
205
|
+
}
|
|
56
206
|
function findServerFnExportReplacements(code, factoryNames) {
|
|
57
207
|
const replacements = [];
|
|
58
208
|
for (const match of code.matchAll(EXPORT_SERVER_FUNCTION_FACTORY_RE)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farm.js/plugin",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.105",
|
|
4
4
|
"description": "Official plugins for Farm.js framework",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Farm.js Team",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"nitro": "3.0.1-alpha.0",
|
|
62
62
|
"picocolors": "^1.0.0",
|
|
63
63
|
"rsc-html-stream": "^0.0.4",
|
|
64
|
-
"@farm.js/core": "0.1.0-beta.
|
|
64
|
+
"@farm.js/core": "0.1.0-beta.105"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
67
67
|
"@types/node": "^20.19.21",
|
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"react-dom": "19.2.8",
|
|
83
83
|
"react-server-dom-webpack": "19.2.8",
|
|
84
84
|
"vite": ">=6.0.0",
|
|
85
|
-
"@farm.js/core": "0.1.0-beta.
|
|
85
|
+
"@farm.js/core": "0.1.0-beta.105"
|
|
86
86
|
},
|
|
87
87
|
"peerDependenciesMeta": {
|
|
88
88
|
"@vitejs/plugin-rsc": {
|