xanix 1.0.5 → 1.0.7
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/bin/bundler/buildClient.js +3 -3
- package/dist/bin/bundler/buildServer.js +2 -3
- package/dist/bin/bundler/cacheNpmModules.js +8 -2
- package/dist/bin/bundler/config/output.js +9 -1
- package/dist/bin/bundler/plugins/XanixDocument/index.d.ts +2 -0
- package/dist/bin/bundler/plugins/XanixDocument/index.js +35 -0
- package/dist/bin/bundler/plugins/XanixEnv.js +13 -3
- package/dist/bin/bundler/plugins/XanixReactRefresh.js +33 -33
- package/dist/bin/bundler/plugins/XanixTransform/transformer copy.d.ts +16 -0
- package/dist/bin/bundler/plugins/XanixTransform/transformer copy.js +631 -0
- package/dist/bin/bundler/plugins/plugins.js +3 -1
- package/dist/bin/bundler/watchClient.js +3 -3
- package/dist/bin/bundler/watchServer.js +2 -3
- package/dist/bin/include/utils.d.ts +3 -0
- package/dist/bin/include/utils.js +17 -0
- package/dist/bin/index.js +0 -0
- package/dist/client-runtime.js +17 -15
- package/dist/components/BaseDocument.d.ts +1 -1
- package/dist/components/BaseDocument.js +1 -3
- package/dist/components/Body.js +3 -1
- package/dist/components/Document.js +3 -1
- package/dist/components/Head.d.ts +1 -1
- package/dist/components/Head.js +37 -34
- package/dist/components/Link.d.ts +1 -1
- package/dist/components/Link.js +23 -18
- package/dist/dataLoader.d.ts +1 -1
- package/dist/dataLoader.js +14 -5
- package/dist/hooks/useData.d.ts +2 -6
- package/dist/hooks/useData.js +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +3 -0
- package/dist/navigate.js +11 -8
- package/dist/server/runtime/xanixPage.js +1 -4
- package/dist/types/assets.d.ts +34 -0
- package/dist/types/express.d.ts +12 -0
- package/dist/types/globals.d.ts +27 -0
- package/dist/types/xanix-virtual.d.ts +26 -0
- package/package.json +75 -73
- package/dist/BaseDocument.d.ts +0 -3
- package/dist/BaseDocument.js +0 -8
- package/dist/bin/bundler/buildCache.d.ts +0 -11
- package/dist/bin/bundler/buildCache.js +0 -112
- package/dist/bin/bundler/plugins/XanixAssets/index.d.ts +0 -4
- package/dist/bin/bundler/plugins/XanixAssets/index.js +0 -22
- package/dist/bin/bundler/plugins/XanixBrowserCodeRemover.d.ts +0 -6
- package/dist/bin/bundler/plugins/XanixBrowserCodeRemover.js +0 -350
- package/dist/bin/bundler/plugins/XanixHydrate.d.ts +0 -3
- package/dist/bin/bundler/plugins/XanixHydrate.js +0 -34
- package/dist/bin/bundler/plugins/commonjs.d.ts +0 -3
- package/dist/bin/bundler/plugins/commonjs.js +0 -10
- package/dist/bin/bundler/plugins/dep-XanixWatchChange.d.ts +0 -9
- package/dist/bin/bundler/plugins/dep-XanixWatchChange.js +0 -27
- package/dist/bin/bundler/plugins/nodeResolve.d.ts +0 -3
- package/dist/bin/bundler/plugins/nodeResolve.js +0 -10
- package/dist/bin/bundler/plugins/rollupEsbuild.d.ts +0 -3
- package/dist/bin/bundler/plugins/rollupEsbuild.js +0 -14
- package/dist/bin/cli/start copy.d.ts +0 -2
- package/dist/bin/cli/start copy.js +0 -34
- package/dist/client/client-runtime.d.ts +0 -10
- package/dist/client/client-runtime.js +0 -98
- package/dist/client/context/RouteContext.d.ts +0 -8
- package/dist/client/context/RouteContext.js +0 -16
- package/dist/client/hooks/usePageProps.d.ts +0 -1
- package/dist/client/hooks/usePageProps.js +0 -2
- package/dist/client/hooks/useRoute.d.ts +0 -1
- package/dist/client/hooks/useRoute.js +0 -2
- package/dist/client/hooks/useSearchParams.d.ts +0 -1
- package/dist/client/hooks/useSearchParams.js +0 -2
- package/dist/client/index.d.ts +0 -2
- package/dist/client/index.js +0 -2
- package/dist/client/navigation.d.ts +0 -3
- package/dist/client/navigation.js +0 -27
- package/dist/client/runtime.d.ts +0 -10
- package/dist/client/runtime.js +0 -98
- package/dist/components/Document/context.d.ts +0 -11
- package/dist/components/Document/context.js +0 -14
- package/dist/components/Document/index.d.ts +0 -8
- package/dist/components/Document/index.js +0 -6
- package/dist/document/BaseDocument.d.ts +0 -3
- package/dist/document/BaseDocument.js +0 -8
- package/dist/document/Body.d.ts +0 -5
- package/dist/document/Body.js +0 -8
- package/dist/document/Document.d.ts +0 -7
- package/dist/document/Document.js +0 -9
- package/dist/document/DocumentContext.d.ts +0 -18
- package/dist/document/DocumentContext.js +0 -6
- package/dist/document/Head.d.ts +0 -5
- package/dist/document/Head.js +0 -41
- package/dist/document/Link.d.ts +0 -7
- package/dist/document/Link.js +0 -28
- package/dist/document/Script.d.ts +0 -3
- package/dist/document/Script.js +0 -5
- package/dist/document/index.d.ts +0 -8
- package/dist/document/index.js +0 -7
- package/dist/document/useDocument.d.ts +0 -2
- package/dist/document/useDocument.js +0 -10
- package/dist/hooks/useCookie.d.ts +0 -2
- package/dist/hooks/useCookie.js +0 -13
- package/dist/hooks/useRequest copy.d.ts +0 -2
- package/dist/hooks/useRequest copy.js +0 -8
- package/dist/navigation/index.d.ts +0 -2
- package/dist/navigation/index.js +0 -2
- package/dist/navigation/navigate.d.ts +0 -7
- package/dist/navigation/navigate.js +0 -31
- package/dist/navigation/useLocation.d.ts +0 -1
- package/dist/navigation/useLocation.js +0 -22
- package/dist/router/index.d.ts +0 -3
- package/dist/router/index.js +0 -3
- package/dist/router/useParams.d.ts +0 -1
- package/dist/router/useParams.js +0 -5
- package/dist/router/usePathname.d.ts +0 -1
- package/dist/router/usePathname.js +0 -8
- package/dist/router/useSearchParams.d.ts +0 -11
- package/dist/router/useSearchParams.js +0 -74
- package/dist/runtime/createServer.d.ts +0 -12
- package/dist/runtime/createServer.js +0 -40
- package/dist/runtime/index.d.ts +0 -12
- package/dist/runtime/index.js +0 -40
- package/dist/runtime/readManifest.d.ts +0 -14
- package/dist/runtime/readManifest.js +0 -8
- package/dist/runtime/xanixPage.d.ts +0 -9
- package/dist/runtime/xanixPage.js +0 -62
- package/dist/server/createServer.d.ts +0 -12
- package/dist/server/createServer.js +0 -40
- package/dist/server/page.d.ts +0 -9
- package/dist/server/page.js +0 -62
- package/dist/server/readManifest.d.ts +0 -14
- package/dist/server/readManifest.js +0 -8
- package/dist/server/runtime copy.d.ts +0 -12
- package/dist/server/runtime copy.js +0 -40
- package/dist/server/runtime.d.ts +0 -12
- package/dist/server/runtime.js +0 -40
- package/dist/server/xanixPage.d.ts +0 -9
- package/dist/server/xanixPage.js +0 -62
- package/dist/types.d.ts +0 -12
- package/dist/types.js +0 -7
- package/dist/utils/index.d.ts +0 -0
- package/dist/utils/index.js +0 -1
- package/dist/xanix.d.ts +0 -3
- package/dist/xanix.js +0 -40
- package/xanix-env.d.ts +0 -79
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import crypto from "node:crypto";
|
|
4
|
+
import { parse } from "@babel/parser";
|
|
5
|
+
import traverse from "@babel/traverse";
|
|
6
|
+
import generate from "@babel/generator";
|
|
7
|
+
import * as t from "@babel/types";
|
|
8
|
+
const RUNTIME_IMPORT = "xanix/runtime";
|
|
9
|
+
const SOURCE_EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mjs", ".cjs"];
|
|
10
|
+
function normalizeFilePath(file) {
|
|
11
|
+
return path.resolve(file).split(path.sep).join("/");
|
|
12
|
+
}
|
|
13
|
+
export function resolveFile(file) {
|
|
14
|
+
const absolute = path.resolve(file);
|
|
15
|
+
if (fs.existsSync(absolute)) {
|
|
16
|
+
const stat = fs.statSync(absolute);
|
|
17
|
+
if (stat.isFile()) {
|
|
18
|
+
return absolute;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
for (const ext of SOURCE_EXTENSIONS) {
|
|
22
|
+
const candidate = `${absolute}${ext}`;
|
|
23
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
24
|
+
return path.resolve(candidate);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (fs.existsSync(absolute) && fs.statSync(absolute).isDirectory()) {
|
|
28
|
+
for (const ext of SOURCE_EXTENSIONS) {
|
|
29
|
+
const candidate = path.join(absolute, `index${ext}`);
|
|
30
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
31
|
+
return path.resolve(candidate);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return absolute;
|
|
36
|
+
}
|
|
37
|
+
export function resolveComponentFile(importer, importPath) {
|
|
38
|
+
if (!importPath.startsWith(".")) {
|
|
39
|
+
return importPath;
|
|
40
|
+
}
|
|
41
|
+
const base = path.resolve(path.dirname(importer), importPath);
|
|
42
|
+
return resolveFile(base);
|
|
43
|
+
}
|
|
44
|
+
export function createPageId(file) {
|
|
45
|
+
return `c_${crypto
|
|
46
|
+
.createHash("sha256")
|
|
47
|
+
.update(path.normalize(file))
|
|
48
|
+
.digest("hex")
|
|
49
|
+
.slice(0, 12)}`;
|
|
50
|
+
}
|
|
51
|
+
export function getJSXComponentName(node) {
|
|
52
|
+
const name = node.openingElement.name;
|
|
53
|
+
if (t.isJSXIdentifier(name)) {
|
|
54
|
+
return name.name;
|
|
55
|
+
}
|
|
56
|
+
if (t.isJSXMemberExpression(name)) {
|
|
57
|
+
if (t.isJSXIdentifier(name.object) && t.isJSXIdentifier(name.property)) {
|
|
58
|
+
return `${name.object.name}.${name.property.name}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
export function findComponentImport(ast, componentName) {
|
|
64
|
+
for (const node of ast.program.body) {
|
|
65
|
+
if (!t.isImportDeclaration(node)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
for (const specifier of node.specifiers) {
|
|
69
|
+
if (t.isImportDefaultSpecifier(specifier) &&
|
|
70
|
+
specifier.local.name === componentName) {
|
|
71
|
+
return {
|
|
72
|
+
file: node.source.value,
|
|
73
|
+
export: "default",
|
|
74
|
+
declaration: node,
|
|
75
|
+
specifier,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
if (t.isImportSpecifier(specifier) &&
|
|
79
|
+
specifier.local.name === componentName) {
|
|
80
|
+
const imported = specifier.imported;
|
|
81
|
+
return {
|
|
82
|
+
file: node.source.value,
|
|
83
|
+
export: t.isIdentifier(imported) ? imported.name : imported.value,
|
|
84
|
+
declaration: node,
|
|
85
|
+
specifier,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Remove a specific component from:
|
|
94
|
+
*
|
|
95
|
+
* import Home from "./Home";
|
|
96
|
+
*
|
|
97
|
+
* or:
|
|
98
|
+
*
|
|
99
|
+
* import Home, { foo } from "./Home";
|
|
100
|
+
*
|
|
101
|
+
* or:
|
|
102
|
+
*
|
|
103
|
+
* import { Home, foo } from "./Home";
|
|
104
|
+
*/
|
|
105
|
+
function removeComponentImport(componentImport) {
|
|
106
|
+
const { declaration, specifier } = componentImport;
|
|
107
|
+
const index = declaration.specifiers.indexOf(specifier);
|
|
108
|
+
if (index !== -1) {
|
|
109
|
+
declaration.specifiers.splice(index, 1);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function removeEmptyImportDeclarations(ast) {
|
|
113
|
+
ast.program.body = ast.program.body.filter((statement) => {
|
|
114
|
+
if (!t.isImportDeclaration(statement)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return statement.specifiers.length > 0;
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create:
|
|
122
|
+
*
|
|
123
|
+
* const Home =
|
|
124
|
+
* (await import("./Home")).default;
|
|
125
|
+
*
|
|
126
|
+
* or:
|
|
127
|
+
*
|
|
128
|
+
* const Home =
|
|
129
|
+
* (await import("./Home")).Home;
|
|
130
|
+
*/
|
|
131
|
+
function createDynamicComponentImport(componentName, importPath, exportName) {
|
|
132
|
+
const importCall = t.callExpression(t.import(), [
|
|
133
|
+
t.stringLiteral(importPath),
|
|
134
|
+
]);
|
|
135
|
+
const awaitedImport = t.awaitExpression(importCall);
|
|
136
|
+
const property = exportName === "default"
|
|
137
|
+
? t.identifier("default")
|
|
138
|
+
: t.identifier(exportName);
|
|
139
|
+
const value = t.memberExpression(awaitedImport, property);
|
|
140
|
+
return t.variableDeclaration("const", [
|
|
141
|
+
t.variableDeclarator(t.identifier(componentName), value),
|
|
142
|
+
]);
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Convert a JSX name into an expression.
|
|
146
|
+
*
|
|
147
|
+
* Example:
|
|
148
|
+
*
|
|
149
|
+
* <Home />
|
|
150
|
+
*
|
|
151
|
+
* becomes:
|
|
152
|
+
*
|
|
153
|
+
* Home
|
|
154
|
+
*/
|
|
155
|
+
function jsxNameToExpression(name) {
|
|
156
|
+
if (t.isJSXIdentifier(name)) {
|
|
157
|
+
return t.identifier(name.name);
|
|
158
|
+
}
|
|
159
|
+
if (t.isJSXMemberExpression(name)) {
|
|
160
|
+
const object = jsxNameToExpressionFromJSXName(name.object);
|
|
161
|
+
const property = jsxNameToExpressionFromJSXName(name.property);
|
|
162
|
+
if (!object || !property) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
return t.memberExpression(object, property);
|
|
166
|
+
}
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
function jsxNameToExpressionFromJSXName(name) {
|
|
170
|
+
if (t.isJSXIdentifier(name)) {
|
|
171
|
+
return t.identifier(name.name);
|
|
172
|
+
}
|
|
173
|
+
if (t.isJSXMemberExpression(name)) {
|
|
174
|
+
const object = jsxNameToExpressionFromJSXName(name.object);
|
|
175
|
+
const property = jsxNameToExpressionFromJSXName(name.property);
|
|
176
|
+
if (!object || !property) {
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
return t.memberExpression(object, property);
|
|
180
|
+
}
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Convert JSX attributes into an object expression.
|
|
185
|
+
*
|
|
186
|
+
* Example:
|
|
187
|
+
*
|
|
188
|
+
* <Home
|
|
189
|
+
* name="Naxrul"
|
|
190
|
+
* age={30}
|
|
191
|
+
* active
|
|
192
|
+
* {...user}
|
|
193
|
+
* />
|
|
194
|
+
*
|
|
195
|
+
* becomes:
|
|
196
|
+
*
|
|
197
|
+
* {
|
|
198
|
+
* name: "Naxrul",
|
|
199
|
+
* age: 30,
|
|
200
|
+
* active: true,
|
|
201
|
+
* ...user
|
|
202
|
+
* }
|
|
203
|
+
*/
|
|
204
|
+
function jsxAttributesToProps(jsx) {
|
|
205
|
+
const properties = [];
|
|
206
|
+
for (const attribute of jsx.openingElement.attributes) {
|
|
207
|
+
/**
|
|
208
|
+
* Handle:
|
|
209
|
+
*
|
|
210
|
+
* {...props}
|
|
211
|
+
*/
|
|
212
|
+
if (t.isJSXSpreadAttribute(attribute)) {
|
|
213
|
+
properties.push(t.spreadElement(attribute.argument));
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Normal JSX attribute:
|
|
218
|
+
*
|
|
219
|
+
* name="value"
|
|
220
|
+
* name={value}
|
|
221
|
+
* active
|
|
222
|
+
*/
|
|
223
|
+
if (!t.isJSXAttribute(attribute)) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
function jsxAttributeNameToExpression(name) {
|
|
227
|
+
if (t.isJSXIdentifier(name)) {
|
|
228
|
+
return t.identifier(name.name);
|
|
229
|
+
}
|
|
230
|
+
return t.stringLiteral(`${name.namespace.name}:${name.name.name}`);
|
|
231
|
+
}
|
|
232
|
+
const key = jsxAttributeNameToExpression(attribute.name);
|
|
233
|
+
/**
|
|
234
|
+
* Boolean JSX prop:
|
|
235
|
+
*
|
|
236
|
+
* <Home active />
|
|
237
|
+
*
|
|
238
|
+
* becomes:
|
|
239
|
+
*
|
|
240
|
+
* active: true
|
|
241
|
+
*/
|
|
242
|
+
if (!attribute.value) {
|
|
243
|
+
properties.push(t.objectProperty(key, t.booleanLiteral(true)));
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* String prop:
|
|
248
|
+
*
|
|
249
|
+
* name="Naxrul"
|
|
250
|
+
*
|
|
251
|
+
* becomes:
|
|
252
|
+
*
|
|
253
|
+
* name: "Naxrul"
|
|
254
|
+
*/
|
|
255
|
+
if (t.isStringLiteral(attribute.value)) {
|
|
256
|
+
properties.push(t.objectProperty(key, t.stringLiteral(attribute.value.value)));
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Expression prop:
|
|
261
|
+
*
|
|
262
|
+
* age={30}
|
|
263
|
+
* user={user}
|
|
264
|
+
* items={items.map(...)}
|
|
265
|
+
*/
|
|
266
|
+
if (t.isJSXExpressionContainer(attribute.value)) {
|
|
267
|
+
const expression = attribute.value.expression;
|
|
268
|
+
if (t.isJSXEmptyExpression(expression)) {
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
properties.push(t.objectProperty(key, expression));
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* JSX element as a prop:
|
|
276
|
+
*
|
|
277
|
+
* content={<div>Hello</div>}
|
|
278
|
+
*/
|
|
279
|
+
if (t.isJSXElement(attribute.value)) {
|
|
280
|
+
properties.push(t.objectProperty(key, attribute.value));
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
return t.objectExpression(properties);
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Convert:
|
|
288
|
+
*
|
|
289
|
+
* <Home name="Naxrul" age={30} />
|
|
290
|
+
*
|
|
291
|
+
* into:
|
|
292
|
+
*
|
|
293
|
+
* {
|
|
294
|
+
* component: Home,
|
|
295
|
+
* props: {
|
|
296
|
+
* name: "Naxrul",
|
|
297
|
+
* age: 30
|
|
298
|
+
* }
|
|
299
|
+
* }
|
|
300
|
+
*/
|
|
301
|
+
function jsxToComponentAndProps(jsx) {
|
|
302
|
+
const component = jsxNameToExpression(jsx.openingElement.name);
|
|
303
|
+
if (!component) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
const props = jsxAttributesToProps(jsx);
|
|
307
|
+
return {
|
|
308
|
+
component,
|
|
309
|
+
props,
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Find:
|
|
314
|
+
*
|
|
315
|
+
* const app = express();
|
|
316
|
+
* const exp = express();
|
|
317
|
+
* const server = express();
|
|
318
|
+
*
|
|
319
|
+
* Returns the variable name.
|
|
320
|
+
*/
|
|
321
|
+
function findExpressAppVariable(ast) {
|
|
322
|
+
for (const statement of ast.program.body) {
|
|
323
|
+
if (!t.isVariableDeclaration(statement)) {
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
for (const declaration of statement.declarations) {
|
|
327
|
+
if (!t.isIdentifier(declaration.id)) {
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!t.isCallExpression(declaration.init)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (!t.isIdentifier(declaration.init.callee, {
|
|
334
|
+
name: "express",
|
|
335
|
+
})) {
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
return declaration.id.name;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return null;
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Replace:
|
|
345
|
+
*
|
|
346
|
+
* const app = express();
|
|
347
|
+
*
|
|
348
|
+
* with:
|
|
349
|
+
*
|
|
350
|
+
* const app = createXanixServer({
|
|
351
|
+
* mode: "watch"
|
|
352
|
+
* });
|
|
353
|
+
*/
|
|
354
|
+
function transformExpressAppTocreateXanixServer(ast, args) {
|
|
355
|
+
const expressVariable = findExpressAppVariable(ast);
|
|
356
|
+
if (!expressVariable) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
for (const statement of ast.program.body) {
|
|
360
|
+
if (!t.isVariableDeclaration(statement)) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
for (const declaration of statement.declarations) {
|
|
364
|
+
if (!t.isIdentifier(declaration.id)) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
if (declaration.id.name !== expressVariable) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
if (!t.isCallExpression(declaration.init)) {
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
if (!t.isIdentifier(declaration.init.callee, {
|
|
374
|
+
name: "express",
|
|
375
|
+
})) {
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
declaration.init = t.callExpression(t.identifier("createXanixServer"), [
|
|
379
|
+
t.objectExpression([
|
|
380
|
+
t.objectProperty(t.identifier("mode"), t.stringLiteral(args.mode)),
|
|
381
|
+
]),
|
|
382
|
+
]);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Remove:
|
|
390
|
+
*
|
|
391
|
+
* import express from "express";
|
|
392
|
+
*
|
|
393
|
+
* once express() has been replaced by
|
|
394
|
+
* createXanixServer().
|
|
395
|
+
*
|
|
396
|
+
* Also handles:
|
|
397
|
+
*
|
|
398
|
+
* import express, { Router } from "express";
|
|
399
|
+
*/
|
|
400
|
+
function removeExpressImport(ast) {
|
|
401
|
+
for (const statement of ast.program.body) {
|
|
402
|
+
if (!t.isImportDeclaration(statement)) {
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
if (statement.source.value !== "express") {
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
statement.specifiers = statement.specifiers.filter((specifier) => {
|
|
409
|
+
return !(t.isImportDefaultSpecifier(specifier) &&
|
|
410
|
+
specifier.local.name === "express");
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
removeEmptyImportDeclarations(ast);
|
|
414
|
+
}
|
|
415
|
+
function hasRuntimeImport(ast, name) {
|
|
416
|
+
for (const statement of ast.program.body) {
|
|
417
|
+
if (!t.isImportDeclaration(statement)) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
if (statement.source.value !== RUNTIME_IMPORT) {
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
for (const specifier of statement.specifiers) {
|
|
424
|
+
if (t.isImportSpecifier(specifier) &&
|
|
425
|
+
t.isIdentifier(specifier.imported) &&
|
|
426
|
+
specifier.imported.name === name) {
|
|
427
|
+
return true;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
function injectRuntimeImport(ast, name) {
|
|
434
|
+
if (hasRuntimeImport(ast, name)) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
// Find existing:
|
|
438
|
+
// import { ... } from "xanix/runtime";
|
|
439
|
+
for (const statement of ast.program.body) {
|
|
440
|
+
if (!t.isImportDeclaration(statement)) {
|
|
441
|
+
continue;
|
|
442
|
+
}
|
|
443
|
+
if (statement.source.value !== RUNTIME_IMPORT) {
|
|
444
|
+
continue;
|
|
445
|
+
}
|
|
446
|
+
statement.specifiers.push(t.importSpecifier(t.identifier(name), t.identifier(name)));
|
|
447
|
+
return;
|
|
448
|
+
}
|
|
449
|
+
// No runtime import exists yet
|
|
450
|
+
ast.program.body.unshift(t.importDeclaration([t.importSpecifier(t.identifier(name), t.identifier(name))], t.stringLiteral(RUNTIME_IMPORT)));
|
|
451
|
+
}
|
|
452
|
+
export function transformer(code, id, mode) {
|
|
453
|
+
const ast = parse(code, {
|
|
454
|
+
sourceType: "module",
|
|
455
|
+
plugins: ["typescript", "jsx"],
|
|
456
|
+
});
|
|
457
|
+
let changed = false;
|
|
458
|
+
traverse(ast, {
|
|
459
|
+
CallExpression(callPath) {
|
|
460
|
+
const call = callPath.node;
|
|
461
|
+
/**
|
|
462
|
+
* Find:
|
|
463
|
+
*
|
|
464
|
+
* res.send(<Home />);
|
|
465
|
+
*/
|
|
466
|
+
if (!t.isMemberExpression(call.callee)) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
if (!t.isIdentifier(call.callee.property, {
|
|
470
|
+
name: "send",
|
|
471
|
+
})) {
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
const argument = call.arguments[0];
|
|
475
|
+
if (!argument || !t.isJSXElement(argument)) {
|
|
476
|
+
return;
|
|
477
|
+
}
|
|
478
|
+
const functionPath = callPath.getFunctionParent();
|
|
479
|
+
if (!functionPath) {
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
const params = functionPath.node.params;
|
|
483
|
+
if (params.length < 2) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const requestParam = params[0];
|
|
487
|
+
const responseParam = params[1];
|
|
488
|
+
if (!t.isIdentifier(requestParam) || !t.isIdentifier(responseParam)) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const componentName = getJSXComponentName(argument);
|
|
492
|
+
if (!componentName) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const componentImport = findComponentImport(ast, componentName);
|
|
496
|
+
if (!componentImport) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
const componentImportPath = componentImport.file;
|
|
500
|
+
const componentFile = resolveComponentFile(id, componentImportPath);
|
|
501
|
+
if (!path.isAbsolute(componentFile)) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const normalizedFile = normalizeFilePath(componentFile);
|
|
505
|
+
const pageId = createPageId(normalizedFile);
|
|
506
|
+
/**
|
|
507
|
+
* Convert:
|
|
508
|
+
*
|
|
509
|
+
* <Home name="Naxrul" age={30} />
|
|
510
|
+
*
|
|
511
|
+
* into:
|
|
512
|
+
*
|
|
513
|
+
* component: Home
|
|
514
|
+
*
|
|
515
|
+
* props: {
|
|
516
|
+
* name: "Naxrul",
|
|
517
|
+
* age: 30
|
|
518
|
+
* }
|
|
519
|
+
*/
|
|
520
|
+
const componentAndProps = jsxToComponentAndProps(argument);
|
|
521
|
+
if (!componentAndProps) {
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
/**
|
|
525
|
+
* Remove:
|
|
526
|
+
*
|
|
527
|
+
* import Home from "./Home";
|
|
528
|
+
*/
|
|
529
|
+
removeComponentImport(componentImport);
|
|
530
|
+
/**
|
|
531
|
+
* Add:
|
|
532
|
+
*
|
|
533
|
+
* const Home =
|
|
534
|
+
* (await import("./Home")).default;
|
|
535
|
+
*/
|
|
536
|
+
const dynamicImport = createDynamicComponentImport(componentName, componentImportPath, componentImport.export);
|
|
537
|
+
/**
|
|
538
|
+
* Insert immediately before:
|
|
539
|
+
*
|
|
540
|
+
* res.send(<Home />);
|
|
541
|
+
*/
|
|
542
|
+
const statementPath = callPath.getStatementParent();
|
|
543
|
+
if (statementPath) {
|
|
544
|
+
statementPath.insertBefore(dynamicImport);
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Route becomes:
|
|
548
|
+
*
|
|
549
|
+
* async (req, res) => {}
|
|
550
|
+
*/
|
|
551
|
+
if (t.isFunction(functionPath.node)) {
|
|
552
|
+
functionPath.node.async = true;
|
|
553
|
+
}
|
|
554
|
+
/**
|
|
555
|
+
* Replace:
|
|
556
|
+
*
|
|
557
|
+
* res.send(<Home />);
|
|
558
|
+
*
|
|
559
|
+
* with:
|
|
560
|
+
*
|
|
561
|
+
* res.send(
|
|
562
|
+
* await xanixPage({
|
|
563
|
+
* clientId,
|
|
564
|
+
* req,
|
|
565
|
+
* res,
|
|
566
|
+
* component: Home,
|
|
567
|
+
* props: {
|
|
568
|
+
* ...
|
|
569
|
+
* }
|
|
570
|
+
* })
|
|
571
|
+
* );
|
|
572
|
+
*/
|
|
573
|
+
const pageCall = t.awaitExpression(t.callExpression(t.identifier("xanixPage"), [
|
|
574
|
+
t.objectExpression([
|
|
575
|
+
t.objectProperty(t.identifier("pageId"), t.stringLiteral(pageId)),
|
|
576
|
+
t.objectProperty(t.identifier("req"), t.identifier(requestParam.name)),
|
|
577
|
+
t.objectProperty(t.identifier("res"), t.identifier(responseParam.name)),
|
|
578
|
+
t.objectProperty(t.identifier("Component"), componentAndProps.component),
|
|
579
|
+
t.objectProperty(t.identifier("props"), componentAndProps.props),
|
|
580
|
+
]),
|
|
581
|
+
]));
|
|
582
|
+
call.arguments[0] = pageCall;
|
|
583
|
+
changed = true;
|
|
584
|
+
},
|
|
585
|
+
});
|
|
586
|
+
if (!changed) {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Change:
|
|
591
|
+
*
|
|
592
|
+
* const app = express();
|
|
593
|
+
*
|
|
594
|
+
* into:
|
|
595
|
+
*
|
|
596
|
+
* const app = createXanixServer();
|
|
597
|
+
*/
|
|
598
|
+
const serverTransformed = transformExpressAppTocreateXanixServer(ast, {
|
|
599
|
+
mode,
|
|
600
|
+
});
|
|
601
|
+
if (serverTransformed) {
|
|
602
|
+
/**
|
|
603
|
+
* Remove:
|
|
604
|
+
*
|
|
605
|
+
* import express from "express";
|
|
606
|
+
*/
|
|
607
|
+
removeExpressImport(ast);
|
|
608
|
+
/**
|
|
609
|
+
* Add:
|
|
610
|
+
*
|
|
611
|
+
* import { createXanixServer }
|
|
612
|
+
* from "xanix/runtime";
|
|
613
|
+
*/
|
|
614
|
+
injectRuntimeImport(ast, "createXanixServer");
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Add:
|
|
618
|
+
*
|
|
619
|
+
* import xanixPage from "xanix/page";
|
|
620
|
+
*/
|
|
621
|
+
injectRuntimeImport(ast, "xanixPage");
|
|
622
|
+
/**
|
|
623
|
+
* Remove empty imports.
|
|
624
|
+
*/
|
|
625
|
+
removeEmptyImportDeclarations(ast);
|
|
626
|
+
const output = generate(ast, {}, code);
|
|
627
|
+
return {
|
|
628
|
+
code: output.code,
|
|
629
|
+
map: output.map,
|
|
630
|
+
};
|
|
631
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import XanixTsconfigAlias from "./XanixTsconfigAlias.js";
|
|
2
|
+
import XanixDocument from "./XanixDocument/index.js";
|
|
2
3
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
|
3
4
|
import commonjs from "@rollup/plugin-commonjs";
|
|
4
5
|
import json from "@rollup/plugin-json";
|
|
@@ -17,7 +18,7 @@ export function xanixDefaultPlugins(options = {}) {
|
|
|
17
18
|
let _plugins = [];
|
|
18
19
|
if (isServer) {
|
|
19
20
|
_plugins.push(XanixTransform({ mode: development ? "watch" : "start" }));
|
|
20
|
-
// _plugins.push(
|
|
21
|
+
// _plugins.push(XanixDocument());
|
|
21
22
|
}
|
|
22
23
|
else {
|
|
23
24
|
if (development) {
|
|
@@ -64,6 +65,7 @@ export function xanixDefaultPlugins(options = {}) {
|
|
|
64
65
|
commonjs(),
|
|
65
66
|
json(),
|
|
66
67
|
xanixUseDataId(),
|
|
68
|
+
XanixDocument(),
|
|
67
69
|
..._plugins,
|
|
68
70
|
esbuild({
|
|
69
71
|
target: isClient ? "es2022" : "node20",
|
|
@@ -3,15 +3,15 @@ import bundlerOutput, { outDir } from "./config/output.js";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import BuildCache from "./cacheNpmModules.js";
|
|
5
5
|
import XanixResolveCacheDeps from "./plugins/XanixResolveCacheDeps/index.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getClientRuntimeFile, getClientRuntimeFileName, } from "../include/utils.js";
|
|
7
7
|
import { xanixDefaultPlugins } from "./plugins/plugins.js";
|
|
8
8
|
const WatchClient = async (entries, options) => {
|
|
9
9
|
const input = {};
|
|
10
10
|
for (const entry of entries) {
|
|
11
11
|
input[entry.name] = entry.file;
|
|
12
12
|
}
|
|
13
|
-
|
|
14
|
-
input[
|
|
13
|
+
const runtimeFileName = getClientRuntimeFileName("development");
|
|
14
|
+
input[runtimeFileName] = getClientRuntimeFile();
|
|
15
15
|
fs.rmSync(outDir.client, {
|
|
16
16
|
recursive: true,
|
|
17
17
|
force: true,
|
|
@@ -4,7 +4,6 @@ import fs from "node:fs";
|
|
|
4
4
|
import { createManifest, readManifest } from "../include/manifest.js";
|
|
5
5
|
import generateClientEntries from "./generateClientEntries.js";
|
|
6
6
|
import bundlerOutput, { outDir } from "./config/output.js";
|
|
7
|
-
import { getDocumentFile } from "../include/utils.js";
|
|
8
7
|
import { entriesEqual } from "../include/entry.js";
|
|
9
8
|
import { xanixDefaultPlugins } from "./plugins/plugins.js";
|
|
10
9
|
const root = process.cwd();
|
|
@@ -20,7 +19,6 @@ const watchServer = async ({ rootEntry, onChange, onBuildEnd, onServerChange, on
|
|
|
20
19
|
await createManifest(entries);
|
|
21
20
|
const input = {
|
|
22
21
|
index: path.resolve(root, rootEntry),
|
|
23
|
-
"xanix-document": await getDocumentFile(),
|
|
24
22
|
};
|
|
25
23
|
const changedFiles = new Set();
|
|
26
24
|
const watcher = watch({
|
|
@@ -42,7 +40,8 @@ const watchServer = async ({ rootEntry, onChange, onBuildEnd, onServerChange, on
|
|
|
42
40
|
path.isAbsolute(id) ||
|
|
43
41
|
id === "@" ||
|
|
44
42
|
id.startsWith("@/") ||
|
|
45
|
-
id.startsWith("xanix")
|
|
43
|
+
id.startsWith("xanix") ||
|
|
44
|
+
id === "virtual:xanix-document") {
|
|
46
45
|
return false;
|
|
47
46
|
}
|
|
48
47
|
return true;
|
|
@@ -1,2 +1,5 @@
|
|
|
1
|
+
export declare function uid(value: string, length?: number): string;
|
|
1
2
|
export declare const getClientRuntimeFile: () => string;
|
|
3
|
+
export declare const getClientRuntimeFileName: (mode: "development" | "production") => string;
|
|
2
4
|
export declare const getDocumentFile: () => Promise<string>;
|
|
5
|
+
export declare const getDocumentFileName: (mode: "development" | "production") => string;
|