@solidjs/start 2.0.0-alpha.2 → 2.0.0-beta.0
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/CHANGELOG.md +35 -0
- package/dist/config/constants.d.ts +3 -3
- package/dist/config/constants.js +1 -1
- package/dist/config/dev-server.js +9 -1
- package/dist/config/env.d.ts +19 -0
- package/dist/config/env.js +81 -0
- package/dist/config/fs-routes/fs-watcher.js +32 -28
- package/dist/config/fs-routes/index.js +2 -2
- package/dist/config/fs-routes/router.d.ts +8 -3
- package/dist/config/fs-routes/router.js +26 -13
- package/dist/config/fs-routes/tree-shake.js +0 -43
- package/dist/config/index.d.ts +16 -1
- package/dist/config/index.js +31 -31
- package/dist/config/lazy.js +4 -2
- package/dist/config/manifest.js +41 -7
- package/dist/config/vite-utils.d.ts +1 -1
- package/dist/directives/bubble-function-declaration.d.ts +3 -0
- package/dist/directives/bubble-function-declaration.js +23 -0
- package/dist/directives/compile.d.ts +9 -0
- package/dist/directives/compile.js +40 -0
- package/dist/directives/generate-unique-name.d.ts +3 -0
- package/dist/directives/generate-unique-name.js +16 -0
- package/dist/directives/get-descriptive-name.d.ts +2 -0
- package/dist/directives/get-descriptive-name.js +37 -0
- package/dist/directives/get-import-identifier.d.ts +4 -0
- package/dist/directives/get-import-identifier.js +19 -0
- package/dist/directives/get-root-statement-path.d.ts +2 -0
- package/dist/directives/get-root-statement-path.js +12 -0
- package/dist/directives/index.d.ts +14 -0
- package/dist/directives/index.js +170 -0
- package/dist/directives/is-statement-top-level.d.ts +3 -0
- package/dist/directives/is-statement-top-level.js +9 -0
- package/dist/directives/paths.d.ts +9 -0
- package/dist/directives/paths.js +36 -0
- package/dist/directives/plugin.d.ts +21 -0
- package/dist/directives/plugin.js +302 -0
- package/dist/directives/remove-unused-variables.d.ts +3 -0
- package/dist/directives/remove-unused-variables.js +66 -0
- package/dist/directives/types.d.ts +10 -0
- package/dist/directives/xxhash32.d.ts +6 -0
- package/dist/directives/xxhash32.js +193 -0
- package/dist/{server/server-runtime.d.ts → fns/client.d.ts} +1 -1
- package/dist/fns/client.js +106 -0
- package/dist/fns/handler.d.ts +5 -0
- package/dist/{server/server-functions-handler.js → fns/handler.js} +55 -96
- package/dist/fns/registration.d.ts +2 -0
- package/dist/fns/registration.js +12 -0
- package/dist/fns/serialization.d.ts +6 -0
- package/dist/fns/serialization.js +208 -0
- package/dist/fns/server.d.ts +7 -0
- package/dist/{server/server-fns-runtime.js → fns/server.js} +7 -1
- package/dist/fns/shared.d.ts +17 -0
- package/dist/fns/shared.js +105 -0
- package/dist/http/index.d.ts +1 -1
- package/dist/middleware/index.d.ts +17 -0
- package/dist/middleware/index.js +15 -0
- package/dist/server/handler.d.ts +61 -3
- package/dist/server/handler.js +18 -16
- package/dist/server/spa/handler.d.ts +30 -1
- package/dist/server/types.d.ts +1 -1
- package/dist/shared/dev-overlay/CodeView.jsx +4 -4
- package/dist/shared/dev-overlay/DevOverlayDialog.jsx +34 -34
- package/dist/shared/dev-overlay/index.jsx +2 -2
- package/dist/shared/dev-overlay/styles.css +40 -40
- package/dist/utils/debounce.d.ts +4 -0
- package/dist/utils/debounce.js +10 -0
- package/package.json +28 -21
- package/src/config/constants.ts +3 -3
- package/src/config/env.ts +105 -0
- package/src/config/fs-routes/fs-watcher.ts +38 -49
- package/src/config/fs-routes/index.ts +2 -2
- package/src/config/fs-routes/router.ts +29 -13
- package/src/config/fs-routes/tree-shake.ts +1 -48
- package/src/config/index.ts +45 -41
- package/src/config/lazy.ts +3 -2
- package/src/config/manifest.ts +45 -10
- package/src/config/vite-utils.ts +1 -1
- package/src/directives/bubble-function-declaration.ts +33 -0
- package/src/directives/compile.ts +54 -0
- package/src/directives/generate-unique-name.ts +22 -0
- package/src/directives/get-descriptive-name.ts +39 -0
- package/src/directives/get-import-identifier.ts +34 -0
- package/src/directives/get-root-statement-path.ts +14 -0
- package/src/directives/index.ts +233 -0
- package/src/directives/is-statement-top-level.ts +13 -0
- package/src/directives/paths.ts +63 -0
- package/src/directives/plugin.ts +389 -0
- package/src/directives/remove-unused-variables.ts +70 -0
- package/src/directives/types.ts +12 -0
- package/src/directives/xxhash32.ts +214 -0
- package/src/env.d.ts +1 -1
- package/src/fns/client.ts +141 -0
- package/src/fns/handler.spec.ts +85 -0
- package/src/{server/server-functions-handler.ts → fns/handler.ts} +65 -117
- package/src/fns/registration.ts +19 -0
- package/src/fns/serialization.ts +250 -0
- package/src/{server/server-fns-runtime.ts → fns/server.ts} +17 -2
- package/src/fns/shared.ts +116 -0
- package/src/middleware/index.ts +17 -0
- package/src/server/handler.ts +19 -17
- package/src/server/types.ts +1 -1
- package/src/shared/dev-overlay/CodeView.tsx +5 -7
- package/src/shared/dev-overlay/DevOverlayDialog.tsx +34 -34
- package/src/shared/dev-overlay/get-source-map.ts +1 -1
- package/src/shared/dev-overlay/index.tsx +4 -4
- package/src/shared/dev-overlay/styles.css +40 -40
- package/src/utils/debounce.ts +10 -0
- package/tsconfig.json +1 -0
- package/dist/server/fetchEvent.spec.js +0 -65
- package/dist/server/server-fns-runtime.d.ts +0 -1
- package/dist/server/server-functions-handler.d.ts +0 -2
- package/dist/server/server-runtime.js +0 -194
- package/src/server/server-runtime.ts +0 -240
- /package/dist/{server/fetchEvent.spec.d.ts → directives/types.js} +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { init, parse } from "es-module-lexer";
|
|
2
2
|
import esbuild from "esbuild";
|
|
3
3
|
import fg from "fast-glob";
|
|
4
|
-
import fs from "fs";
|
|
4
|
+
import fs from "node:fs";
|
|
5
5
|
import micromatch from "micromatch";
|
|
6
|
-
import { posix } from "path";
|
|
6
|
+
import { posix } from "node:path";
|
|
7
7
|
import { pathToRegexp } from "path-to-regexp";
|
|
8
8
|
|
|
9
9
|
import { normalizePath } from "vite";
|
|
@@ -32,6 +32,8 @@ export function analyzeModule(src: string) {
|
|
|
32
32
|
);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
type RouterEvent = CustomEvent<{ route: string; type: "update" | "remove" | "add" }>;
|
|
36
|
+
|
|
35
37
|
export class BaseFileSystemRouter extends EventTarget {
|
|
36
38
|
routes: Route[];
|
|
37
39
|
|
|
@@ -98,8 +100,11 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
98
100
|
update = undefined;
|
|
99
101
|
|
|
100
102
|
_addRoute(route: Route) {
|
|
101
|
-
|
|
103
|
+
const idx = this.routes.findIndex(r => r.path === route.path);
|
|
104
|
+
if (idx >= 0) this.routes.splice(idx, 1);
|
|
102
105
|
this.routes.push(route);
|
|
106
|
+
|
|
107
|
+
return idx >= 0;
|
|
103
108
|
}
|
|
104
109
|
|
|
105
110
|
async addRoute(src: string) {
|
|
@@ -109,7 +114,7 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
109
114
|
const route = this.toRoute(src);
|
|
110
115
|
if (route) {
|
|
111
116
|
this._addRoute(route);
|
|
112
|
-
this.reload(route.path);
|
|
117
|
+
this.reload(route.path, "add");
|
|
113
118
|
}
|
|
114
119
|
} catch (e) {
|
|
115
120
|
console.error(e);
|
|
@@ -117,25 +122,27 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
117
122
|
}
|
|
118
123
|
}
|
|
119
124
|
|
|
120
|
-
reload(route: string) {
|
|
125
|
+
reload(route: string, type: "update" | "remove" | "add") {
|
|
121
126
|
this.dispatchEvent(
|
|
122
|
-
new
|
|
123
|
-
// @ts-ignore
|
|
127
|
+
new CustomEvent("reload", {
|
|
124
128
|
detail: {
|
|
125
129
|
route,
|
|
130
|
+
type,
|
|
126
131
|
},
|
|
127
132
|
}),
|
|
128
133
|
);
|
|
129
134
|
}
|
|
130
135
|
|
|
131
|
-
async updateRoute(
|
|
132
|
-
src = normalizePath(
|
|
136
|
+
async updateRoute(src_: string) {
|
|
137
|
+
const src = normalizePath(src_);
|
|
133
138
|
if (this.isRoute(src)) {
|
|
134
139
|
try {
|
|
135
140
|
const route = this.toRoute(src);
|
|
136
141
|
if (route) {
|
|
137
|
-
this._addRoute(route);
|
|
138
|
-
this.reload(route.path);
|
|
142
|
+
const updated = this._addRoute(route);
|
|
143
|
+
this.reload(route.path, updated ? "update" : "add");
|
|
144
|
+
} else {
|
|
145
|
+
this.removeRoute(src_);
|
|
139
146
|
}
|
|
140
147
|
} catch (e) {
|
|
141
148
|
console.error(e);
|
|
@@ -151,11 +158,20 @@ export class BaseFileSystemRouter extends EventTarget {
|
|
|
151
158
|
if (path === undefined) {
|
|
152
159
|
return;
|
|
153
160
|
}
|
|
154
|
-
|
|
155
|
-
this.
|
|
161
|
+
|
|
162
|
+
const idx = this.routes.findIndex(r => r.path === path);
|
|
163
|
+
if (idx === -1) return;
|
|
164
|
+
|
|
165
|
+
this.routes.splice(idx, 1);
|
|
166
|
+
this.reload(path, "remove");
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
169
|
|
|
170
|
+
on(type: string, cb: (evt: RouterEvent) => void) {
|
|
171
|
+
this.addEventListener(type, cb as any);
|
|
172
|
+
return () => this.removeEventListener(type, cb as any);
|
|
173
|
+
}
|
|
174
|
+
|
|
159
175
|
buildRoutesPromise?: Promise<any[]>;
|
|
160
176
|
|
|
161
177
|
async getRoutes() {
|
|
@@ -6,7 +6,7 @@ import type * as Babel from "@babel/core";
|
|
|
6
6
|
import type { NodePath, PluginObj, PluginPass } from "@babel/core";
|
|
7
7
|
import type { Binding } from "@babel/traverse";
|
|
8
8
|
import { basename } from "pathe";
|
|
9
|
-
import type { Plugin
|
|
9
|
+
import type { Plugin } from "vite";
|
|
10
10
|
|
|
11
11
|
type State = Omit<PluginPass, "opts"> & {
|
|
12
12
|
opts: { pick: string[] };
|
|
@@ -280,10 +280,6 @@ function treeShakeTransform({ types: t }: typeof Babel): PluginObj<State> {
|
|
|
280
280
|
}
|
|
281
281
|
|
|
282
282
|
export function treeShake(): Plugin {
|
|
283
|
-
let config: ResolvedConfig;
|
|
284
|
-
const cache: Record<string, any> = {};
|
|
285
|
-
let server: ViteDevServer;
|
|
286
|
-
|
|
287
283
|
async function transform(id: string, code: string) {
|
|
288
284
|
const [path, queryString] = id.split("?");
|
|
289
285
|
const query = new URLSearchParams(queryString);
|
|
@@ -308,46 +304,6 @@ export function treeShake(): Plugin {
|
|
|
308
304
|
return {
|
|
309
305
|
name: "tree-shake",
|
|
310
306
|
enforce: "pre",
|
|
311
|
-
configResolved(resolvedConfig) {
|
|
312
|
-
config = resolvedConfig;
|
|
313
|
-
},
|
|
314
|
-
configureServer(s) {
|
|
315
|
-
server = s;
|
|
316
|
-
},
|
|
317
|
-
async handleHotUpdate(ctx) {
|
|
318
|
-
if (cache[ctx.file]) {
|
|
319
|
-
const mods = [];
|
|
320
|
-
const newCode = await ctx.read();
|
|
321
|
-
for (const [id, code] of Object.entries(cache[ctx.file])) {
|
|
322
|
-
const transformed = await transform(id, newCode);
|
|
323
|
-
if (!transformed) continue;
|
|
324
|
-
|
|
325
|
-
const { code: transformedCode } = transformed;
|
|
326
|
-
|
|
327
|
-
if (transformedCode !== code) {
|
|
328
|
-
const mod = server.moduleGraph.getModuleById(id);
|
|
329
|
-
if (mod) mods.push(mod);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
cache[ctx.file] ??= {};
|
|
333
|
-
cache[ctx.file][id] = transformedCode;
|
|
334
|
-
// server.moduleGraph.setModuleSource(id, code);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
return mods;
|
|
338
|
-
}
|
|
339
|
-
// const mods = [];
|
|
340
|
-
// [...server.moduleGraph.urlToModuleMap.entries()].forEach(([url, m]) => {
|
|
341
|
-
// if (m.file === ctx.file && m.id.includes("pick=")) {
|
|
342
|
-
// if (!m.id.includes("pick=loader")) {
|
|
343
|
-
// mods.push(m);
|
|
344
|
-
// }
|
|
345
|
-
// }
|
|
346
|
-
// });
|
|
347
|
-
// return mods;
|
|
348
|
-
// // this.router.updateRoute(ctx.path);
|
|
349
|
-
// }
|
|
350
|
-
},
|
|
351
307
|
async transform(code, id) {
|
|
352
308
|
const [path, queryString] = id.split("?");
|
|
353
309
|
if (!path) return;
|
|
@@ -358,9 +314,6 @@ export function treeShake(): Plugin {
|
|
|
358
314
|
const transformed = await transform(id, code);
|
|
359
315
|
if (!transformed?.code) return;
|
|
360
316
|
|
|
361
|
-
cache[path] ??= {};
|
|
362
|
-
cache[path][id] = transformed.code;
|
|
363
|
-
|
|
364
317
|
return {
|
|
365
318
|
code: transformed.code,
|
|
366
319
|
map: transformed.map,
|
package/src/config/index.ts
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { TanStackServerFnPlugin } from "@tanstack/server-functions-plugin";
|
|
2
1
|
import { defu } from "defu";
|
|
3
2
|
import { globSync } from "node:fs";
|
|
4
3
|
import { extname, isAbsolute, join } from "node:path";
|
|
5
|
-
import {
|
|
6
|
-
import { normalizePath, type PluginOption } from "vite";
|
|
4
|
+
import type { PluginOption } from "vite";
|
|
7
5
|
import solid, { type Options as SolidOptions } from "vite-plugin-solid";
|
|
8
|
-
|
|
6
|
+
import { type ServerFunctionsOptions, serverFunctionsPlugin } from "../directives/index.ts";
|
|
9
7
|
import { DEFAULT_EXTENSIONS, VIRTUAL_MODULES, VITE_ENVIRONMENTS } from "./constants.ts";
|
|
10
8
|
import { devServer } from "./dev-server.ts";
|
|
9
|
+
import { envPlugin, type EnvPluginOptions } from "./env.ts";
|
|
11
10
|
import { SolidStartClientFileRouter, SolidStartServerFileRouter } from "./fs-router.ts";
|
|
12
11
|
import { fsRoutes } from "./fs-routes/index.ts";
|
|
13
12
|
import type { BaseFileSystemRouter } from "./fs-routes/router.ts";
|
|
@@ -21,6 +20,19 @@ export interface SolidStartOptions {
|
|
|
21
20
|
routeDir?: string;
|
|
22
21
|
extensions?: string[];
|
|
23
22
|
middleware?: string;
|
|
23
|
+
serialization?: {
|
|
24
|
+
/**
|
|
25
|
+
* The serialization mode to use for server functions/actions.
|
|
26
|
+
* The "js" mode uses a custom binary format that is more efficient than JSON, but requires a custom deserializer (with `eval()`) on the client.
|
|
27
|
+
* A strong CSP should block `eval()` executions, which would prevent the "js" mode from working.
|
|
28
|
+
* The "json" mode uses JSON for serialization, which is less efficient but can be deserialized with `JSON.parse` on the client.
|
|
29
|
+
*
|
|
30
|
+
* @default "json"
|
|
31
|
+
*/
|
|
32
|
+
mode?: "js" | "json";
|
|
33
|
+
};
|
|
34
|
+
env?: EnvPluginOptions;
|
|
35
|
+
serverFunctions?: Pick<ServerFunctionsOptions, "filter">;
|
|
24
36
|
}
|
|
25
37
|
|
|
26
38
|
const absolute = (path: string, root: string) =>
|
|
@@ -51,6 +63,18 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
|
|
|
51
63
|
server: `${start.appRoot}/entry-server${entryExtension}`,
|
|
52
64
|
};
|
|
53
65
|
return [
|
|
66
|
+
// TODO (Alexis): check if the comment below is still relevant
|
|
67
|
+
//
|
|
68
|
+
// Must be placed after fsRoutes, as treeShake will remove the
|
|
69
|
+
// server fn exports added in by this plugin
|
|
70
|
+
serverFunctionsPlugin({
|
|
71
|
+
manifest: VIRTUAL_MODULES.serverFnManifest,
|
|
72
|
+
runtime: {
|
|
73
|
+
server: '@solidjs/start/fns/server',
|
|
74
|
+
client: '@solidjs/start/fns/client',
|
|
75
|
+
},
|
|
76
|
+
filter: options?.serverFunctions?.filter,
|
|
77
|
+
}),
|
|
54
78
|
{
|
|
55
79
|
name: "solid-start:config",
|
|
56
80
|
enforce: "pre",
|
|
@@ -80,6 +104,16 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
|
|
|
80
104
|
return {
|
|
81
105
|
appType: "custom",
|
|
82
106
|
build: { assetsDir: "_build/assets" },
|
|
107
|
+
optimizeDeps: {
|
|
108
|
+
// Suppress TS errors from Vite 7 types when configuring Vite 8's Rolldown
|
|
109
|
+
...({
|
|
110
|
+
rolldownOptions: {
|
|
111
|
+
transform: {
|
|
112
|
+
jsx: "react",
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
} as any),
|
|
116
|
+
},
|
|
83
117
|
environments: {
|
|
84
118
|
[VITE_ENVIRONMENTS.client]: {
|
|
85
119
|
consumer: "client",
|
|
@@ -122,6 +156,9 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
|
|
|
122
156
|
}
|
|
123
157
|
: {}),
|
|
124
158
|
},
|
|
159
|
+
// Depending on the package manager and dependency structure Vite externalizes @solidjs/start
|
|
160
|
+
// This makes sure that @solidjs/start goes through the Vite build process
|
|
161
|
+
noExternal: ["@solidjs/start"],
|
|
125
162
|
},
|
|
126
163
|
define: {
|
|
127
164
|
"import.meta.env.MANIFEST": `globalThis.MANIFEST`,
|
|
@@ -131,6 +168,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
|
|
|
131
168
|
"import.meta.env.START_APP_ENTRY": JSON.stringify(appEntryPath),
|
|
132
169
|
"import.meta.env.START_CLIENT_ENTRY": JSON.stringify(handlers.client),
|
|
133
170
|
"import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay),
|
|
171
|
+
"import.meta.env.SEROVAL_MODE": JSON.stringify(start.serialization?.mode || "json"),
|
|
134
172
|
},
|
|
135
173
|
builder: {
|
|
136
174
|
sharedPlugins: true,
|
|
@@ -163,42 +201,7 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
|
|
|
163
201
|
},
|
|
164
202
|
}),
|
|
165
203
|
lazy(),
|
|
166
|
-
|
|
167
|
-
// server fn exports added in by this plugin
|
|
168
|
-
TanStackServerFnPlugin({
|
|
169
|
-
// This is the ID that will be available to look up and import
|
|
170
|
-
// our server function manifest and resolve its module
|
|
171
|
-
manifestVirtualImportId: VIRTUAL_MODULES.serverFnManifest,
|
|
172
|
-
directive: "use server",
|
|
173
|
-
callers: [
|
|
174
|
-
{
|
|
175
|
-
envConsumer: "client",
|
|
176
|
-
envName: VITE_ENVIRONMENTS.client,
|
|
177
|
-
getRuntimeCode: () =>
|
|
178
|
-
`import { createServerReference } from "${normalizePath(
|
|
179
|
-
fileURLToPath(new URL("../server/server-runtime", import.meta.url))
|
|
180
|
-
)}"`,
|
|
181
|
-
replacer: opts => `createServerReference('${opts.functionId}')`,
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
envConsumer: "server",
|
|
185
|
-
envName: VITE_ENVIRONMENTS.server,
|
|
186
|
-
getRuntimeCode: () =>
|
|
187
|
-
`import { createServerReference } from '${normalizePath(
|
|
188
|
-
fileURLToPath(new URL("../server/server-fns-runtime", import.meta.url))
|
|
189
|
-
)}'`,
|
|
190
|
-
replacer: opts => `createServerReference(${opts.fn}, '${opts.functionId}')`,
|
|
191
|
-
},
|
|
192
|
-
],
|
|
193
|
-
provider: {
|
|
194
|
-
envName: VITE_ENVIRONMENTS.server,
|
|
195
|
-
getRuntimeCode: () =>
|
|
196
|
-
`import { createServerReference } from '${normalizePath(
|
|
197
|
-
fileURLToPath(new URL("../server/server-fns-runtime", import.meta.url))
|
|
198
|
-
)}'`,
|
|
199
|
-
replacer: opts => `createServerReference(${opts.fn}, '${opts.functionId}')`,
|
|
200
|
-
},
|
|
201
|
-
}),
|
|
204
|
+
envPlugin(options?.env),
|
|
202
205
|
{
|
|
203
206
|
name: "solid-start:virtual-modules",
|
|
204
207
|
async resolveId(id) {
|
|
@@ -221,8 +224,9 @@ export function solidStart(options?: SolidStartOptions): Array<PluginOption> {
|
|
|
221
224
|
{
|
|
222
225
|
name: "solid-start:capture-client-bundle",
|
|
223
226
|
enforce: "post",
|
|
224
|
-
generateBundle(
|
|
227
|
+
generateBundle(options, bundle) {
|
|
225
228
|
globalThis.START_CLIENT_BUNDLE = bundle;
|
|
229
|
+
(globalThis as any).START_CLIENT_OUT_DIR = options.dir;
|
|
226
230
|
},
|
|
227
231
|
},
|
|
228
232
|
devServer(),
|
package/src/config/lazy.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { PluginItem } from "@babel/core";
|
|
2
2
|
import babel from "@babel/core";
|
|
3
3
|
import * as t from "@babel/types";
|
|
4
|
-
import { sep as osSep } from "path";
|
|
5
|
-
import { basename, relative, sep } from "path/posix";
|
|
4
|
+
import { sep as osSep } from "node:path";
|
|
5
|
+
import { basename, relative, sep } from "node:path/posix";
|
|
6
6
|
import type { PluginOption } from "vite";
|
|
7
7
|
import { VITE_ENVIRONMENTS } from "./constants.ts";
|
|
8
8
|
|
|
@@ -90,6 +90,7 @@ const lazy = (): PluginOption => {
|
|
|
90
90
|
// The transformed files either import "lazy" or css files
|
|
91
91
|
// Therefore we skip, if the src doesn't have any import
|
|
92
92
|
if (src.indexOf("import") === -1) return;
|
|
93
|
+
if (id.includes("entry-server")) return;
|
|
93
94
|
|
|
94
95
|
const plugins: PluginItem[] = [];
|
|
95
96
|
|
package/src/config/manifest.ts
CHANGED
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { type PluginOption, type ViteDevServer, version as viteVersion } from "vite";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
2
5
|
|
|
3
6
|
import { findStylesInModuleGraph } from "../server/collect-styles.ts";
|
|
4
7
|
import { VIRTUAL_MODULES } from "./constants.ts";
|
|
@@ -18,12 +21,16 @@ export function manifest(start: SolidStartOptions): PluginOption {
|
|
|
18
21
|
return `\0${VIRTUAL_MODULES.clientViteManifest}`;
|
|
19
22
|
if (id === VIRTUAL_MODULES.getClientManifest)
|
|
20
23
|
return this.resolve(
|
|
21
|
-
new URL("../server/manifest/client-manifest", import.meta.url)
|
|
24
|
+
fileURLToPath(new URL("../server/manifest/client-manifest", import.meta.url)),
|
|
22
25
|
);
|
|
23
26
|
if (id === VIRTUAL_MODULES.getManifest) {
|
|
24
27
|
return this.environment.config.consumer === "client"
|
|
25
|
-
? this.resolve(
|
|
26
|
-
|
|
28
|
+
? this.resolve(
|
|
29
|
+
fileURLToPath(new URL("../server/manifest/client-manifest", import.meta.url)),
|
|
30
|
+
)
|
|
31
|
+
: this.resolve(
|
|
32
|
+
fileURLToPath(new URL("../server/manifest/ssr-manifest", import.meta.url)),
|
|
33
|
+
);
|
|
27
34
|
}
|
|
28
35
|
if (id === VIRTUAL_MODULES.middleware) {
|
|
29
36
|
if (start.middleware) return await this.resolve(start.middleware);
|
|
@@ -40,9 +47,37 @@ export function manifest(start: SolidStartOptions): PluginOption {
|
|
|
40
47
|
v => "isEntry" in v && v.isEntry,
|
|
41
48
|
);
|
|
42
49
|
if (!entry) throw new Error("No client entry found");
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
);
|
|
50
|
+
let rawManifest: string | undefined;
|
|
51
|
+
|
|
52
|
+
const viteMajor = parseInt(viteVersion.split('.')[0]!, 10);
|
|
53
|
+
|
|
54
|
+
const manifestKey = Object.keys(globalThis.START_CLIENT_BUNDLE).find(k => k.endsWith("manifest.json"));
|
|
55
|
+
if (manifestKey && viteMajor < 8) {
|
|
56
|
+
const manifestAsset = globalThis.START_CLIENT_BUNDLE[manifestKey] as any;
|
|
57
|
+
rawManifest = manifestAsset.source as string;
|
|
58
|
+
} else {
|
|
59
|
+
try {
|
|
60
|
+
const appRoot = (start as any).appRoot || "./src";
|
|
61
|
+
let outDir = ".solid-start/client";
|
|
62
|
+
if (devServer?.environments?.client?.config?.build?.outDir) {
|
|
63
|
+
outDir = devServer.environments.client.config.build.outDir;
|
|
64
|
+
} else if (this.environment?.config?.build?.outDir && this.environment?.config?.consumer === "client") {
|
|
65
|
+
outDir = this.environment.config.build.outDir;
|
|
66
|
+
} else if ((globalThis as any).START_CLIENT_OUT_DIR) {
|
|
67
|
+
outDir = (globalThis as any).START_CLIENT_OUT_DIR;
|
|
68
|
+
}
|
|
69
|
+
const manifestPath = path.resolve(appRoot, "..", outDir, ".vite/manifest.json");
|
|
70
|
+
rawManifest = fs.readFileSync(manifestPath, "utf-8");
|
|
71
|
+
} catch (e) {
|
|
72
|
+
throw new Error(`Manifest asset not found in bundle and could not be read from disk. Keys: ${Object.keys(globalThis.START_CLIENT_BUNDLE).join(", ")}. Error: ${e}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!rawManifest) {
|
|
77
|
+
throw new Error("Failed to extract or read raw manifest.");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
clientViteManifest = JSON.parse(rawManifest);
|
|
46
81
|
}
|
|
47
82
|
return `export const clientViteManifest = ${JSON.stringify(clientViteManifest)};`;
|
|
48
83
|
} else if (id === `\0${VIRTUAL_MODULES.middleware}`) return "export default {};";
|
|
@@ -50,11 +85,11 @@ export function manifest(start: SolidStartOptions): PluginOption {
|
|
|
50
85
|
if (this.environment.mode !== "dev")
|
|
51
86
|
throw new Error("@manifest queries are only allowed in dev");
|
|
52
87
|
|
|
53
|
-
const [
|
|
88
|
+
const [urlPath, query] = id.split("?");
|
|
54
89
|
const target = id.split("/")[2]!;
|
|
55
90
|
const params = new URLSearchParams(query);
|
|
56
|
-
if (!
|
|
57
|
-
if (
|
|
91
|
+
if (!urlPath || !query) return;
|
|
92
|
+
if (urlPath.endsWith("assets")) {
|
|
58
93
|
const id = params.get("id");
|
|
59
94
|
if (!id) {
|
|
60
95
|
throw new Error("Missing id to get assets.");
|
package/src/config/vite-utils.ts
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type * as babel from "@babel/core";
|
|
2
|
+
import * as t from "@babel/types";
|
|
3
|
+
|
|
4
|
+
export function bubbleFunctionDeclaration(path: babel.NodePath<t.FunctionDeclaration>): void {
|
|
5
|
+
const decl = path.node;
|
|
6
|
+
// Check if declaration is FunctionDeclaration
|
|
7
|
+
if (decl.id) {
|
|
8
|
+
const block = (path.findParent(current => current.isBlockStatement()) ||
|
|
9
|
+
path.scope.getProgramParent().path) as babel.NodePath<t.BlockStatement>;
|
|
10
|
+
|
|
11
|
+
if (path.parentPath.isExportNamedDeclaration()) {
|
|
12
|
+
path.parentPath.replaceWith(
|
|
13
|
+
t.exportNamedDeclaration(undefined, [t.exportSpecifier(decl.id, decl.id)]),
|
|
14
|
+
);
|
|
15
|
+
} else if (path.parentPath.isExportDefaultDeclaration()) {
|
|
16
|
+
path.replaceWith(decl.id);
|
|
17
|
+
} else {
|
|
18
|
+
path.remove();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const [tmp] = block.unshiftContainer(
|
|
22
|
+
"body",
|
|
23
|
+
t.variableDeclaration("const", [
|
|
24
|
+
t.variableDeclarator(
|
|
25
|
+
decl.id,
|
|
26
|
+
t.functionExpression(decl.id, decl.params, decl.body, decl.generator, decl.async),
|
|
27
|
+
),
|
|
28
|
+
]),
|
|
29
|
+
);
|
|
30
|
+
block.scope.registerDeclaration(tmp);
|
|
31
|
+
tmp.skip();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import * as babel from "@babel/core";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { directivesPlugin, type StateContext } from "./plugin.ts";
|
|
4
|
+
import xxHash32 from "./xxhash32.ts";
|
|
5
|
+
|
|
6
|
+
export interface CompileResult {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
code: string;
|
|
9
|
+
map: babel.BabelFileResult["map"];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export type CompileOptions = Omit<StateContext, "count" | "hash" | "imports" | "valid">;
|
|
13
|
+
|
|
14
|
+
export async function compile(
|
|
15
|
+
id: string,
|
|
16
|
+
code: string,
|
|
17
|
+
options: CompileOptions,
|
|
18
|
+
): Promise<CompileResult> {
|
|
19
|
+
const context: StateContext = {
|
|
20
|
+
...options,
|
|
21
|
+
valid: false,
|
|
22
|
+
hash: xxHash32(id).toString(16),
|
|
23
|
+
count: 0,
|
|
24
|
+
imports: new Map(),
|
|
25
|
+
};
|
|
26
|
+
const pluginOption = [directivesPlugin, context];
|
|
27
|
+
const plugins: NonNullable<NonNullable<babel.TransformOptions["parserOpts"]>["plugins"]> = [
|
|
28
|
+
"jsx",
|
|
29
|
+
];
|
|
30
|
+
if (/\.[mc]?tsx?$/i.test(id)) {
|
|
31
|
+
plugins.push("typescript");
|
|
32
|
+
}
|
|
33
|
+
const result = await babel.transformAsync(code, {
|
|
34
|
+
plugins: [pluginOption],
|
|
35
|
+
parserOpts: {
|
|
36
|
+
plugins,
|
|
37
|
+
},
|
|
38
|
+
filename: path.basename(id),
|
|
39
|
+
ast: false,
|
|
40
|
+
sourceMaps: true,
|
|
41
|
+
configFile: false,
|
|
42
|
+
babelrc: false,
|
|
43
|
+
sourceFileName: id,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
if (result) {
|
|
47
|
+
return {
|
|
48
|
+
valid: context.valid,
|
|
49
|
+
code: result.code || "",
|
|
50
|
+
map: result.map,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
throw new Error("invariant");
|
|
54
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as babel from "@babel/core";
|
|
2
|
+
import * as t from "@babel/types";
|
|
3
|
+
|
|
4
|
+
export function generateUniqueName(path: babel.NodePath, name: string): t.Identifier {
|
|
5
|
+
let uid: string;
|
|
6
|
+
let i = 1;
|
|
7
|
+
do {
|
|
8
|
+
uid = name + "_" + i;
|
|
9
|
+
i++;
|
|
10
|
+
} while (
|
|
11
|
+
path.scope.hasLabel(uid) ||
|
|
12
|
+
path.scope.hasBinding(uid) ||
|
|
13
|
+
path.scope.hasGlobal(uid) ||
|
|
14
|
+
path.scope.hasReference(uid)
|
|
15
|
+
);
|
|
16
|
+
|
|
17
|
+
const program = path.scope.getProgramParent();
|
|
18
|
+
program.references[uid] = true;
|
|
19
|
+
program.uids[uid] = true;
|
|
20
|
+
|
|
21
|
+
return t.identifier(uid);
|
|
22
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { NodePath } from "@babel/core";
|
|
2
|
+
|
|
3
|
+
export function getDescriptiveName(path: NodePath, defaultName: string): string {
|
|
4
|
+
let current: NodePath | null = path;
|
|
5
|
+
while (current) {
|
|
6
|
+
switch (current.node.type) {
|
|
7
|
+
case "FunctionDeclaration":
|
|
8
|
+
case "FunctionExpression": {
|
|
9
|
+
if (current.node.id) {
|
|
10
|
+
return current.node.id.name;
|
|
11
|
+
}
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
case "VariableDeclarator": {
|
|
15
|
+
if (current.node.id.type === "Identifier") {
|
|
16
|
+
return current.node.id.name;
|
|
17
|
+
}
|
|
18
|
+
break;
|
|
19
|
+
}
|
|
20
|
+
case "ClassPrivateMethod":
|
|
21
|
+
case "ClassMethod":
|
|
22
|
+
case "ObjectMethod": {
|
|
23
|
+
switch (current.node.key.type) {
|
|
24
|
+
case "Identifier":
|
|
25
|
+
return current.node.key.name;
|
|
26
|
+
case "PrivateName":
|
|
27
|
+
return current.node.key.id.name;
|
|
28
|
+
default:
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
default:
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
current = current.parentPath;
|
|
37
|
+
}
|
|
38
|
+
return defaultName;
|
|
39
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type * as babel from "@babel/core";
|
|
2
|
+
import * as t from "@babel/types";
|
|
3
|
+
import { generateUniqueName } from "./generate-unique-name.ts";
|
|
4
|
+
import type { ImportDefinition } from "./types.ts";
|
|
5
|
+
|
|
6
|
+
export function getImportIdentifier(
|
|
7
|
+
imports: Map<string, t.Identifier>,
|
|
8
|
+
path: babel.NodePath,
|
|
9
|
+
registration: ImportDefinition,
|
|
10
|
+
): t.Identifier {
|
|
11
|
+
const name = registration.kind === "named" ? registration.name : "default";
|
|
12
|
+
const target = `${registration.source}[${name}]`;
|
|
13
|
+
const current = imports.get(target);
|
|
14
|
+
if (current) {
|
|
15
|
+
return current;
|
|
16
|
+
}
|
|
17
|
+
const programParent = path.scope.getProgramParent();
|
|
18
|
+
const uid = generateUniqueName(programParent.path, name);
|
|
19
|
+
programParent.registerDeclaration(
|
|
20
|
+
(programParent.path as babel.NodePath<t.Program>).unshiftContainer(
|
|
21
|
+
"body",
|
|
22
|
+
t.importDeclaration(
|
|
23
|
+
[
|
|
24
|
+
registration.kind === "named"
|
|
25
|
+
? t.importSpecifier(uid, t.identifier(registration.name))
|
|
26
|
+
: t.importDefaultSpecifier(uid),
|
|
27
|
+
],
|
|
28
|
+
t.stringLiteral(registration.source),
|
|
29
|
+
),
|
|
30
|
+
)[0],
|
|
31
|
+
);
|
|
32
|
+
imports.set(target, uid);
|
|
33
|
+
return uid;
|
|
34
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type * as babel from "@babel/core";
|
|
2
|
+
import * as t from "@babel/types";
|
|
3
|
+
|
|
4
|
+
export function getRootStatementPath(path: babel.NodePath): babel.NodePath {
|
|
5
|
+
let current = path.parentPath;
|
|
6
|
+
while (current) {
|
|
7
|
+
const next = current.parentPath;
|
|
8
|
+
if (next && t.isProgram(next.node)) {
|
|
9
|
+
return current;
|
|
10
|
+
}
|
|
11
|
+
current = next;
|
|
12
|
+
}
|
|
13
|
+
return path;
|
|
14
|
+
}
|