@solidjs/start 2.0.0-beta.8 → 2.0.0-rc.1
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 +36 -1
- package/dist/config/boundary-modules.d.ts +14 -0
- package/dist/config/boundary-modules.js +41 -0
- package/dist/config/env.js +11 -8
- package/dist/config/fs-routes/tree-shake.js +175 -32
- package/dist/config/index.js +31 -2
- package/dist/fns/handler.js +13 -2
- package/dist/http/http.d.ts +81 -0
- package/dist/http/http.js +129 -0
- package/dist/http/index.d.ts +2 -81
- package/dist/http/index.js +6 -129
- package/dist/middleware/index.d.ts +1 -0
- package/dist/middleware/index.js +5 -1
- package/dist/server/StartServer.jsx +1 -1
- package/dist/server/fetchEvent.d.ts +10 -0
- package/dist/server/fetchEvent.js +10 -0
- package/dist/server/handler.js +7 -11
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.jsx +1 -0
- package/dist/server/spa/StartServer.jsx +1 -1
- package/dist/server/strip-path-base.d.ts +1 -0
- package/dist/server/strip-path-base.js +10 -0
- package/dist/server/web-stream.d.ts +9 -0
- package/dist/server/web-stream.js +26 -0
- package/dist/shared/HttpHeader.jsx +1 -1
- package/dist/shared/dev-overlay/createStackFrame.js +46 -12
- package/env.d.ts +12 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,41 @@
|
|
|
1
1
|
# @solidjs/start
|
|
2
2
|
|
|
3
|
+
## 2.0.0-rc.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 02e561e: Expose experimental `decorateHandler` and `decorateMiddleware` helpers from
|
|
8
|
+
`@solidjs/start/server` for providing Solid's request context to custom h3 handlers and
|
|
9
|
+
middleware.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- f66b5a9: fix: keep non-picked route exports that picked exports reference instead of deleting their bindings, so API handlers can call helpers exported from the same file (#2100); also fixes picked exports declared via `export { ... }` specifiers being dropped (#1659)
|
|
14
|
+
- 1763296: fix: run `onBeforeResponse` middleware in declared order (#2131)
|
|
15
|
+
|
|
16
|
+
Applications that reversed their `onBeforeResponse` arrays as a workaround should restore the intended declaration order.
|
|
17
|
+
|
|
18
|
+
- cd98e7d: Fix API route matching when `server.baseURL` is configured.
|
|
19
|
+
|
|
20
|
+
## 2.0.0-beta.10
|
|
21
|
+
|
|
22
|
+
### Patch Changes
|
|
23
|
+
|
|
24
|
+
- 8af6304: Support for Vite 8.1 Experimental Bundled Dev Mode
|
|
25
|
+
- 9d91484: fix: don't crash when a server function throws an error whose message contains non-latin1 characters.
|
|
26
|
+
- 068b64c: Return a cancellation-safe web `ReadableStream` for streaming SSR in development. Returning Solid's
|
|
27
|
+
raw stream only rendered on Node; Bun and Deno coerced it to `[object Object]`.
|
|
28
|
+
|
|
29
|
+
## 2.0.0-beta.9
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- f3e84ec: Add support for `server-only` and `client-only` modules
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- a8a2328: fix: mark `@solidjs/start/http` and `@solidjs/start/middleware` as `server-only` so importing them from client-reachable code fails loudly at dev/build time, instead of silently shipping them to the browser where they crashed hydration and broke unrelated actions/forms with no diagnostic (#2068)
|
|
38
|
+
|
|
3
39
|
## 2.0.0-beta.8
|
|
4
40
|
|
|
5
41
|
### Patch Changes
|
|
@@ -108,7 +144,6 @@
|
|
|
108
144
|
- 8256190: Rework `@solidjs/start/env`
|
|
109
145
|
- 6cbba24: Fix multiple Set-Cookie headers being lost on redirect responses
|
|
110
146
|
- d4cc548: ## Bump Seroval
|
|
111
|
-
|
|
112
147
|
- version `1.4.1`
|
|
113
148
|
|
|
114
149
|
- dd40610: Handle base url in api routes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
/**
|
|
3
|
+
* Supports `server-only` and `client-only` marker modules (#2162): importing
|
|
4
|
+
* `server-only` from a client module (or `client-only` from a server module)
|
|
5
|
+
* fails at resolve time; in the allowed environment the marker resolves to an
|
|
6
|
+
* empty module.
|
|
7
|
+
*
|
|
8
|
+
* Start's own server-only entry points (`@solidjs/start/http`,
|
|
9
|
+
* `@solidjs/start/middleware`) import `server-only` themselves, so pulling
|
|
10
|
+
* them into the client bundle fails loudly instead of shipping server code to
|
|
11
|
+
* the browser, where it crashed hydration and broke unrelated actions/forms
|
|
12
|
+
* with no diagnostic (https://github.com/solidjs/solid-start/issues/2068).
|
|
13
|
+
*/
|
|
14
|
+
export declare function boundaryModules(): Plugin;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const VIRTUAL_ID = "\0solid-start:boundary-modules:id";
|
|
2
|
+
/**
|
|
3
|
+
* Supports `server-only` and `client-only` marker modules (#2162): importing
|
|
4
|
+
* `server-only` from a client module (or `client-only` from a server module)
|
|
5
|
+
* fails at resolve time; in the allowed environment the marker resolves to an
|
|
6
|
+
* empty module.
|
|
7
|
+
*
|
|
8
|
+
* Start's own server-only entry points (`@solidjs/start/http`,
|
|
9
|
+
* `@solidjs/start/middleware`) import `server-only` themselves, so pulling
|
|
10
|
+
* them into the client bundle fails loudly instead of shipping server code to
|
|
11
|
+
* the browser, where it crashed hydration and broke unrelated actions/forms
|
|
12
|
+
* with no diagnostic (https://github.com/solidjs/solid-start/issues/2068).
|
|
13
|
+
*/
|
|
14
|
+
export function boundaryModules() {
|
|
15
|
+
return {
|
|
16
|
+
name: "solid-start:boundary-modules",
|
|
17
|
+
enforce: "pre",
|
|
18
|
+
resolveId(id, importer, { ssr }) {
|
|
19
|
+
if (id === "server-only") {
|
|
20
|
+
if (!ssr)
|
|
21
|
+
this.error(`Attempt to import 'server-only' in a client module: ${importer}. ` +
|
|
22
|
+
`Code that uses this module must run only on the server: mark it with ` +
|
|
23
|
+
`"use server", or make sure it is only imported by server code.`);
|
|
24
|
+
}
|
|
25
|
+
else if (id === "client-only") {
|
|
26
|
+
if (ssr)
|
|
27
|
+
this.error(`Attempt to import 'client-only' in a server module: ${importer}. ` +
|
|
28
|
+
`Code that uses this module must run only in the browser: make sure it ` +
|
|
29
|
+
`is only imported by client code (e.g. wrap components with clientOnly()).`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return VIRTUAL_ID;
|
|
35
|
+
},
|
|
36
|
+
load(id) {
|
|
37
|
+
if (id === VIRTUAL_ID)
|
|
38
|
+
return "export {}";
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
package/dist/config/env.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { loadEnv } from "vite";
|
|
2
|
+
import { parseIdQuery } from "./utils.js";
|
|
2
3
|
const LOADERS = {
|
|
3
4
|
node: `export default key => process.env[key];`,
|
|
4
5
|
"cloudflare-workers": `import { env } from 'cloudflare:workers';export default key => env[key];`,
|
|
@@ -39,17 +40,19 @@ export function envPlugin(options) {
|
|
|
39
40
|
env = config.mode !== "production" ? "development" : "production";
|
|
40
41
|
},
|
|
41
42
|
resolveId(id) {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
const { filename } = parseIdQuery(id);
|
|
44
|
+
if (filename === SERVER_ENV ||
|
|
45
|
+
filename === CLIENT_ENV ||
|
|
46
|
+
filename === SERVER_RUNTIME_ENV ||
|
|
47
|
+
filename === SERVER_RUNTIME_LOADER) {
|
|
46
48
|
return id;
|
|
47
49
|
}
|
|
48
50
|
return undefined;
|
|
49
51
|
},
|
|
50
52
|
load(id) {
|
|
53
|
+
const { filename } = parseIdQuery(id);
|
|
51
54
|
const isServer = this.environment.config.consumer === "server";
|
|
52
|
-
if (
|
|
55
|
+
if (filename === SERVER_ENV) {
|
|
53
56
|
if (!isServer) {
|
|
54
57
|
return SERVER_ONLY_MODULE;
|
|
55
58
|
}
|
|
@@ -58,19 +61,19 @@ export function envPlugin(options) {
|
|
|
58
61
|
: loadEnv(env, '.', serverPrefix);
|
|
59
62
|
return convertObjectToModule(vars);
|
|
60
63
|
}
|
|
61
|
-
if (
|
|
64
|
+
if (filename === CLIENT_ENV) {
|
|
62
65
|
const vars = currentOptions.client?.load
|
|
63
66
|
? currentOptions.client.load(env)
|
|
64
67
|
: loadEnv(env, '.', clientPrefix);
|
|
65
68
|
return convertObjectToModule(vars);
|
|
66
69
|
}
|
|
67
|
-
if (
|
|
70
|
+
if (filename === SERVER_RUNTIME_LOADER) {
|
|
68
71
|
if (!isServer) {
|
|
69
72
|
return SERVER_ONLY_MODULE;
|
|
70
73
|
}
|
|
71
74
|
return runtimeCode;
|
|
72
75
|
}
|
|
73
|
-
if (
|
|
76
|
+
if (filename === SERVER_RUNTIME_ENV) {
|
|
74
77
|
if (!isServer) {
|
|
75
78
|
return SERVER_ONLY_MODULE;
|
|
76
79
|
}
|
|
@@ -20,13 +20,28 @@ function treeShakeTransform({ types: t }) {
|
|
|
20
20
|
}
|
|
21
21
|
return path.node.id && path.node.id.type === "Identifier" ? path.get("id") : null;
|
|
22
22
|
}
|
|
23
|
-
function
|
|
23
|
+
function isTypeOnlyReference(path) {
|
|
24
|
+
return path.getAncestry().some(parent => {
|
|
25
|
+
if (parent.isTSType())
|
|
26
|
+
return true;
|
|
27
|
+
if (parent.isExportNamedDeclaration())
|
|
28
|
+
return parent.node.exportKind === "type";
|
|
29
|
+
if (parent.isExportSpecifier())
|
|
30
|
+
return parent.node.exportKind === "type";
|
|
31
|
+
return false;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
function runtimeReferences(binding) {
|
|
35
|
+
return binding.referencePaths.filter(ref => !isTypeOnlyReference(ref));
|
|
36
|
+
}
|
|
37
|
+
function isIdentifierReferenced(ident, includeTypes = false) {
|
|
24
38
|
const b = ident.scope.getBinding(ident.node.name);
|
|
25
|
-
if (b
|
|
39
|
+
if (b) {
|
|
40
|
+
const references = b.constantViolations.concat(includeTypes ? b.referencePaths : runtimeReferences(b));
|
|
41
|
+
if (!references.length)
|
|
42
|
+
return false;
|
|
26
43
|
if (b.path.type === "FunctionDeclaration") {
|
|
27
|
-
return !b.
|
|
28
|
-
.concat(b.referencePaths)
|
|
29
|
-
.every(ref => ref.findParent(p => p === b.path));
|
|
44
|
+
return !references.every(ref => ref.findParent(p => p === b.path));
|
|
30
45
|
}
|
|
31
46
|
return true;
|
|
32
47
|
}
|
|
@@ -34,13 +49,13 @@ function treeShakeTransform({ types: t }) {
|
|
|
34
49
|
}
|
|
35
50
|
function markFunction(path, state) {
|
|
36
51
|
const ident = getIdentifier(path);
|
|
37
|
-
if (ident && ident.node && isIdentifierReferenced(ident)) {
|
|
52
|
+
if (ident && ident.node && isIdentifierReferenced(ident, true)) {
|
|
38
53
|
state.refs.add(ident);
|
|
39
54
|
}
|
|
40
55
|
}
|
|
41
56
|
function markImport(path, state) {
|
|
42
57
|
const local = path.get("local");
|
|
43
|
-
if (isIdentifierReferenced(local)) {
|
|
58
|
+
if (isIdentifierReferenced(local, true)) {
|
|
44
59
|
state.refs.add(local);
|
|
45
60
|
}
|
|
46
61
|
}
|
|
@@ -49,12 +64,13 @@ function treeShakeTransform({ types: t }) {
|
|
|
49
64
|
Program: {
|
|
50
65
|
enter(path, state) {
|
|
51
66
|
state.refs = new Set();
|
|
67
|
+
state.candidates = new Set();
|
|
52
68
|
state.done = false;
|
|
53
69
|
path.traverse({
|
|
54
70
|
VariableDeclarator(variablePath, variableState) {
|
|
55
71
|
if (variablePath.node.id.type === "Identifier") {
|
|
56
72
|
const local = variablePath.get("id");
|
|
57
|
-
if (isIdentifierReferenced(local)) {
|
|
73
|
+
if (isIdentifierReferenced(local, true)) {
|
|
58
74
|
variableState.refs.add(local);
|
|
59
75
|
}
|
|
60
76
|
}
|
|
@@ -69,7 +85,7 @@ function treeShakeTransform({ types: t }) {
|
|
|
69
85
|
: (() => {
|
|
70
86
|
throw new Error("invariant");
|
|
71
87
|
})());
|
|
72
|
-
if (isIdentifierReferenced(local)) {
|
|
88
|
+
if (isIdentifierReferenced(local, true)) {
|
|
73
89
|
variableState.refs.add(local);
|
|
74
90
|
}
|
|
75
91
|
});
|
|
@@ -88,29 +104,41 @@ function treeShakeTransform({ types: t }) {
|
|
|
88
104
|
else {
|
|
89
105
|
return;
|
|
90
106
|
}
|
|
91
|
-
if (isIdentifierReferenced(local)) {
|
|
107
|
+
if (isIdentifierReferenced(local, true)) {
|
|
92
108
|
variableState.refs.add(local);
|
|
93
109
|
}
|
|
94
110
|
});
|
|
95
111
|
}
|
|
96
112
|
},
|
|
97
113
|
ExportDefaultDeclaration(exportNamedPath) {
|
|
98
|
-
// if opts.keep is true, we don't remove the routeData export
|
|
99
114
|
if (state.opts.pick && !state.opts.pick.includes("default")) {
|
|
100
|
-
exportNamedPath.
|
|
115
|
+
const decl = exportNamedPath.node.declaration;
|
|
116
|
+
// A named function/class declaration creates a module-scope
|
|
117
|
+
// binding that picked exports may reference, so only strip
|
|
118
|
+
// the `export default`; the sweep below removes the
|
|
119
|
+
// declaration again if nothing references it.
|
|
120
|
+
if ((t.isFunctionDeclaration(decl) || t.isClassDeclaration(decl)) && decl.id) {
|
|
121
|
+
const [newPath] = exportNamedPath.replaceWith(decl);
|
|
122
|
+
const id = newPath.get("id");
|
|
123
|
+
state.refs.add(id);
|
|
124
|
+
state.candidates.add(id);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
exportNamedPath.remove();
|
|
128
|
+
}
|
|
101
129
|
}
|
|
102
130
|
},
|
|
103
131
|
ExportNamedDeclaration(exportNamedPath) {
|
|
104
|
-
//
|
|
132
|
+
// No pick list means nothing gets pruned.
|
|
105
133
|
if (!state.opts.pick) {
|
|
106
134
|
return;
|
|
107
135
|
}
|
|
108
136
|
const specifiers = exportNamedPath.get("specifiers");
|
|
109
137
|
if (specifiers.length) {
|
|
110
138
|
specifiers.forEach(s => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
139
|
+
const exported = s.node.exported;
|
|
140
|
+
const exportedName = t.isIdentifier(exported) ? exported.name : exported.value;
|
|
141
|
+
if (!state.opts.pick.includes(exportedName)) {
|
|
114
142
|
s.remove();
|
|
115
143
|
}
|
|
116
144
|
});
|
|
@@ -124,24 +152,51 @@ function treeShakeTransform({ types: t }) {
|
|
|
124
152
|
return;
|
|
125
153
|
}
|
|
126
154
|
switch (decl.node.type) {
|
|
127
|
-
case "FunctionDeclaration":
|
|
155
|
+
case "FunctionDeclaration":
|
|
156
|
+
case "ClassDeclaration": {
|
|
128
157
|
const name = decl.node.id?.name;
|
|
129
158
|
if (name && state.opts.pick && !state.opts.pick.includes(name)) {
|
|
130
|
-
|
|
159
|
+
// Keep the declaration in module scope since picked
|
|
160
|
+
// exports may reference it; the sweep below removes it
|
|
161
|
+
// again if unreferenced.
|
|
162
|
+
const [newPath] = exportNamedPath.replaceWith(decl.node);
|
|
163
|
+
const id = newPath.get("id");
|
|
164
|
+
state.refs.add(id);
|
|
165
|
+
state.candidates.add(id);
|
|
131
166
|
}
|
|
132
167
|
break;
|
|
133
168
|
}
|
|
134
169
|
case "VariableDeclaration": {
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
170
|
+
const declNode = decl.node;
|
|
171
|
+
// Destructuring declarators are conservatively treated as
|
|
172
|
+
// picked (matching the previous behavior of leaving them
|
|
173
|
+
// untouched).
|
|
174
|
+
const isPicked = (d) => d.id.type !== "Identifier" || state.opts.pick.includes(d.id.name);
|
|
175
|
+
if (declNode.declarations.every(isPicked)) {
|
|
176
|
+
break;
|
|
177
|
+
}
|
|
178
|
+
// Split into one declaration per declarator (preserving
|
|
179
|
+
// evaluation order) and export only the picked ones.
|
|
180
|
+
// Unpicked bindings are kept since picked exports may
|
|
181
|
+
// reference them; the sweep below removes them again if
|
|
182
|
+
// unreferenced.
|
|
183
|
+
const statements = declNode.declarations.map(d => {
|
|
184
|
+
const single = t.variableDeclaration(declNode.kind, [d]);
|
|
185
|
+
single.declare = declNode.declare;
|
|
186
|
+
return isPicked(d) ? t.exportNamedDeclaration(single, []) : single;
|
|
144
187
|
});
|
|
188
|
+
const newPaths = exportNamedPath.replaceWithMultiple(statements);
|
|
189
|
+
for (const p of newPaths) {
|
|
190
|
+
if (!p.isVariableDeclaration())
|
|
191
|
+
continue;
|
|
192
|
+
for (const d of p.get("declarations")) {
|
|
193
|
+
if (d.node.id.type === "Identifier") {
|
|
194
|
+
const id = d.get("id");
|
|
195
|
+
state.refs.add(id);
|
|
196
|
+
state.candidates.add(id);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
145
200
|
break;
|
|
146
201
|
}
|
|
147
202
|
default: {
|
|
@@ -152,6 +207,8 @@ function treeShakeTransform({ types: t }) {
|
|
|
152
207
|
FunctionDeclaration: markFunction,
|
|
153
208
|
FunctionExpression: markFunction,
|
|
154
209
|
ArrowFunctionExpression: markFunction,
|
|
210
|
+
ClassDeclaration: markFunction,
|
|
211
|
+
ClassExpression: markFunction,
|
|
155
212
|
ImportSpecifier: markImport,
|
|
156
213
|
ImportDefaultSpecifier: markImport,
|
|
157
214
|
ImportNamespaceSpecifier: markImport,
|
|
@@ -163,11 +220,95 @@ function treeShakeTransform({ types: t }) {
|
|
|
163
220
|
}
|
|
164
221
|
},
|
|
165
222
|
}, state);
|
|
223
|
+
// References alone cannot distinguish live bindings from dead cycles.
|
|
224
|
+
// Build a graph of localized exports and retain only those reachable
|
|
225
|
+
// from runtime code outside that graph.
|
|
226
|
+
path.scope.crawl();
|
|
227
|
+
const tracked = new Map();
|
|
228
|
+
const candidateOwners = new Map();
|
|
229
|
+
const addOwner = (owners, binding, owner) => {
|
|
230
|
+
const paths = owners.get(binding) ?? new Set();
|
|
231
|
+
paths.add(owner);
|
|
232
|
+
owners.set(binding, paths);
|
|
233
|
+
};
|
|
234
|
+
for (const identifier of state.refs) {
|
|
235
|
+
if (identifier.node?.type !== "Identifier" || !identifier.parentPath)
|
|
236
|
+
continue;
|
|
237
|
+
const binding = identifier.scope.getBinding(identifier.node.name);
|
|
238
|
+
if (binding)
|
|
239
|
+
addOwner(tracked, binding, identifier.parentPath);
|
|
240
|
+
}
|
|
241
|
+
for (const identifier of state.candidates) {
|
|
242
|
+
if (!identifier.parentPath)
|
|
243
|
+
continue;
|
|
244
|
+
const binding = identifier.scope.getBinding(identifier.node.name);
|
|
245
|
+
if (!binding)
|
|
246
|
+
continue;
|
|
247
|
+
addOwner(tracked, binding, identifier.parentPath);
|
|
248
|
+
addOwner(candidateOwners, binding, identifier.parentPath);
|
|
249
|
+
}
|
|
250
|
+
const dependencies = new Map();
|
|
251
|
+
const live = new Set();
|
|
252
|
+
for (const [binding, owners] of tracked) {
|
|
253
|
+
dependencies.set(binding, new Set());
|
|
254
|
+
if ([...owners].some(owner => owner.findParent(parent => parent.isExportNamedDeclaration() || parent.isExportDefaultDeclaration()))) {
|
|
255
|
+
live.add(binding);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const ownerBindings = new Map();
|
|
259
|
+
for (const [binding, owners] of tracked) {
|
|
260
|
+
for (const owner of owners) {
|
|
261
|
+
ownerBindings.set(owner, binding);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const owningBinding = (reference) => {
|
|
265
|
+
const owner = reference.find(parent => ownerBindings.has(parent));
|
|
266
|
+
return owner && ownerBindings.get(owner);
|
|
267
|
+
};
|
|
268
|
+
for (const target of tracked.keys()) {
|
|
269
|
+
const references = target.constantViolations.concat(runtimeReferences(target));
|
|
270
|
+
for (const reference of references) {
|
|
271
|
+
const source = owningBinding(reference);
|
|
272
|
+
if (source)
|
|
273
|
+
dependencies.get(source)?.add(target);
|
|
274
|
+
else
|
|
275
|
+
live.add(target);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
const pending = [...live];
|
|
279
|
+
while (pending.length) {
|
|
280
|
+
const source = pending.pop();
|
|
281
|
+
for (const dependency of dependencies.get(source) ?? []) {
|
|
282
|
+
if (live.has(dependency))
|
|
283
|
+
continue;
|
|
284
|
+
live.add(dependency);
|
|
285
|
+
pending.push(dependency);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
const deadCandidates = new Set([...candidateOwners.keys()].filter(candidate => !live.has(candidate)));
|
|
289
|
+
path.traverse({
|
|
290
|
+
VariableDeclarator(variablePath) {
|
|
291
|
+
if (variablePath.node.id.type !== "Identifier")
|
|
292
|
+
return;
|
|
293
|
+
const binding = variablePath.scope.getBinding(variablePath.node.id.name);
|
|
294
|
+
if (binding && deadCandidates.has(binding))
|
|
295
|
+
variablePath.remove();
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
for (const [candidate, owners] of candidateOwners) {
|
|
299
|
+
if (!deadCandidates.has(candidate))
|
|
300
|
+
continue;
|
|
301
|
+
for (const owner of owners) {
|
|
302
|
+
if (!owner.removed && !owner.isVariableDeclarator())
|
|
303
|
+
owner.remove();
|
|
304
|
+
}
|
|
305
|
+
}
|
|
166
306
|
const refs = state.refs;
|
|
307
|
+
const refNodes = new Set([...refs].map(ref => ref.node));
|
|
167
308
|
let count = 0;
|
|
168
309
|
const sweepFunction = (sweepPath) => {
|
|
169
310
|
const ident = getIdentifier(sweepPath);
|
|
170
|
-
if (ident && ident.node &&
|
|
311
|
+
if (ident && ident.node && refNodes.has(ident.node) && !isIdentifierReferenced(ident)) {
|
|
171
312
|
++count;
|
|
172
313
|
if (t.isAssignmentExpression(sweepPath.parentPath) ||
|
|
173
314
|
t.isVariableDeclarator(sweepPath.parentPath)) {
|
|
@@ -180,7 +321,7 @@ function treeShakeTransform({ types: t }) {
|
|
|
180
321
|
};
|
|
181
322
|
function sweepImport(sweepPath) {
|
|
182
323
|
const local = sweepPath.get("local");
|
|
183
|
-
if (
|
|
324
|
+
if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
|
|
184
325
|
++count;
|
|
185
326
|
sweepPath.remove();
|
|
186
327
|
if (sweepPath.parent.specifiers.length === 0) {
|
|
@@ -195,7 +336,7 @@ function treeShakeTransform({ types: t }) {
|
|
|
195
336
|
VariableDeclarator(variablePath) {
|
|
196
337
|
if (variablePath.node.id.type === "Identifier") {
|
|
197
338
|
const local = variablePath.get("id");
|
|
198
|
-
if (
|
|
339
|
+
if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
|
|
199
340
|
++count;
|
|
200
341
|
variablePath.remove();
|
|
201
342
|
}
|
|
@@ -212,7 +353,7 @@ function treeShakeTransform({ types: t }) {
|
|
|
212
353
|
: (() => {
|
|
213
354
|
throw new Error("invariant");
|
|
214
355
|
})());
|
|
215
|
-
if (
|
|
356
|
+
if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
|
|
216
357
|
++count;
|
|
217
358
|
p.remove();
|
|
218
359
|
}
|
|
@@ -236,7 +377,7 @@ function treeShakeTransform({ types: t }) {
|
|
|
236
377
|
else {
|
|
237
378
|
return;
|
|
238
379
|
}
|
|
239
|
-
if (
|
|
380
|
+
if (refNodes.has(local.node) && !isIdentifierReferenced(local)) {
|
|
240
381
|
++count;
|
|
241
382
|
e.remove();
|
|
242
383
|
}
|
|
@@ -249,6 +390,8 @@ function treeShakeTransform({ types: t }) {
|
|
|
249
390
|
FunctionDeclaration: sweepFunction,
|
|
250
391
|
FunctionExpression: sweepFunction,
|
|
251
392
|
ArrowFunctionExpression: sweepFunction,
|
|
393
|
+
ClassDeclaration: sweepFunction,
|
|
394
|
+
ClassExpression: sweepFunction,
|
|
252
395
|
ImportSpecifier: sweepImport,
|
|
253
396
|
ImportDefaultSpecifier: sweepImport,
|
|
254
397
|
ImportNamespaceSpecifier: sweepImport,
|
package/dist/config/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { defu } from "defu";
|
|
2
2
|
import { globSync } from "node:fs";
|
|
3
|
-
import { extname, isAbsolute, join } from "node:path";
|
|
3
|
+
import { basename, extname, isAbsolute, join } from "node:path";
|
|
4
4
|
import solid from "vite-plugin-solid";
|
|
5
5
|
import { serverFunctionsPlugin } from "../directives/index.js";
|
|
6
|
+
import { boundaryModules } from "./boundary-modules.js";
|
|
6
7
|
import { DEFAULT_EXTENSIONS, VIRTUAL_MODULES, VITE_ENVIRONMENTS } from "./constants.js";
|
|
7
8
|
import { devServer } from "./dev-server.js";
|
|
8
9
|
import { envPlugin } from "./env.js";
|
|
@@ -60,8 +61,11 @@ export function solidStart(options) {
|
|
|
60
61
|
},
|
|
61
62
|
};
|
|
62
63
|
},
|
|
63
|
-
async config(
|
|
64
|
+
async config(config, env) {
|
|
64
65
|
const clientInput = [handlers.client];
|
|
66
|
+
const clientEntryUrl = env.command === "serve" && config.experimental?.bundledDev
|
|
67
|
+
? `assets/${basename(handlers.client, entryExtension)}.js`
|
|
68
|
+
: handlers.client;
|
|
65
69
|
if (env.command === "build") {
|
|
66
70
|
const clientRouter = globalThis.ROUTERS.client;
|
|
67
71
|
for (const route of await clientRouter.getRoutes()) {
|
|
@@ -148,7 +152,9 @@ export function solidStart(options) {
|
|
|
148
152
|
// esbuild receives a valid JS string literal for the define value
|
|
149
153
|
"import.meta.env.START_APP_ENTRY": JSON.stringify(appEntryPath),
|
|
150
154
|
"import.meta.env.START_CLIENT_ENTRY": JSON.stringify(handlers.client),
|
|
155
|
+
"import.meta.env.START_CLIENT_ENTRY_URL": JSON.stringify(clientEntryUrl),
|
|
151
156
|
"import.meta.env.START_DEV_OVERLAY": JSON.stringify(start.devOverlay),
|
|
157
|
+
"import.meta.env.SERVER_BASE_URL": JSON.stringify(config.server?.baseURL ?? ""),
|
|
152
158
|
"import.meta.env.SEROVAL_MODE": JSON.stringify(start.serialization?.mode || "json"),
|
|
153
159
|
},
|
|
154
160
|
builder: {
|
|
@@ -185,6 +191,29 @@ export function solidStart(options) {
|
|
|
185
191
|
}),
|
|
186
192
|
lazy(),
|
|
187
193
|
envPlugin(options?.env),
|
|
194
|
+
boundaryModules(),
|
|
195
|
+
{
|
|
196
|
+
name: "solid-start:boundary-modules",
|
|
197
|
+
enforce: "pre",
|
|
198
|
+
resolveId(id, importer, { ssr }) {
|
|
199
|
+
if (id === "server-only") {
|
|
200
|
+
if (!ssr)
|
|
201
|
+
this.error(`Attempt to import 'server-only' in a client module: ${importer}`);
|
|
202
|
+
}
|
|
203
|
+
else if (id === "client-only") {
|
|
204
|
+
if (ssr)
|
|
205
|
+
this.error(`Attempt to import 'client-only' in a server module: ${importer}`);
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
return "\0solid-start:boundary-modules:id";
|
|
211
|
+
},
|
|
212
|
+
load(id) {
|
|
213
|
+
if (id === "\0solid-start:boundary-modules:id")
|
|
214
|
+
return "export {}";
|
|
215
|
+
},
|
|
216
|
+
},
|
|
188
217
|
{
|
|
189
218
|
name: "solid-start:virtual-modules",
|
|
190
219
|
async resolveId(id) {
|
package/dist/fns/handler.js
CHANGED
|
@@ -144,7 +144,7 @@ export async function handleServerFunction(h3Event) {
|
|
|
144
144
|
}
|
|
145
145
|
else if (instance) {
|
|
146
146
|
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
|
|
147
|
-
h3Event.res.headers.set("X-Error", error
|
|
147
|
+
h3Event.res.headers.set("X-Error", toHeaderValue(error));
|
|
148
148
|
}
|
|
149
149
|
else {
|
|
150
150
|
x = handleNoJS(x, request, parsed, true);
|
|
@@ -158,7 +158,7 @@ export async function handleServerFunction(h3Event) {
|
|
|
158
158
|
headers.set("X-Error", errorHeader);
|
|
159
159
|
}
|
|
160
160
|
return new Response(body.body, {
|
|
161
|
-
headers
|
|
161
|
+
headers,
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
164
|
h3Event.res.headers.set(BODY_FORMAT_KEY, BodyFormat.Seroval);
|
|
@@ -172,6 +172,17 @@ export async function handleServerFunction(h3Event) {
|
|
|
172
172
|
return x;
|
|
173
173
|
}
|
|
174
174
|
}
|
|
175
|
+
// header values must be ByteString-safe (no chars > 0xFF), otherwise Headers.set throws
|
|
176
|
+
function toHeaderValue(value) {
|
|
177
|
+
const stripped = value.replace(/[\r\n]+/g, "");
|
|
178
|
+
try {
|
|
179
|
+
return /[^\x00-\xFF]/.test(stripped) ? encodeURIComponent(stripped) : stripped;
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
// encodeURIComponent throws on lone surrogates
|
|
183
|
+
return "true";
|
|
184
|
+
}
|
|
185
|
+
}
|
|
175
186
|
function handleNoJS(result, request, parsed, thrown) {
|
|
176
187
|
const url = new URL(request.url);
|
|
177
188
|
const isError = result instanceof Error;
|