@solidjs/vite-plugin 3.0.0-next.36 → 3.0.0-next.38
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/README.md +47 -8
- package/dist/cjs/index.cjs +462 -97
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +464 -99
- package/dist/esm/index.mjs.map +1 -1
- package/dist/types/src/diagnostics/index.d.ts +14 -1
- package/dist/types/src/index.d.ts +16 -8
- package/dist/types/src/server-functions/compile.d.ts +2 -0
- package/dist/types/src/server-functions/index.d.ts +17 -10
- package/dist/types/src/ssr/index.d.ts +12 -5
- package/dist/types/src/tsrx.d.ts +11 -0
- package/package.json +3 -3
package/dist/esm/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import * as babel from '@babel/core';
|
|
2
2
|
import remapping from '@ampproject/remapping';
|
|
3
3
|
import solid from '@solidjs/babel-plugin';
|
|
4
|
-
import { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
4
|
+
import fs, { existsSync, readFileSync, mkdirSync, writeFileSync, rmSync } from 'fs';
|
|
5
5
|
import { mergeAndConcat } from 'merge-anything';
|
|
6
6
|
import { createRequire } from 'module';
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { Readable } from 'node:stream';
|
|
9
|
-
import { createFilter, normalizePath, loadEnv, runnerImport, defaultClientConditions, defaultServerConditions, defaultExternalConditions } from 'vite';
|
|
9
|
+
import { createFilter, normalizePath, loadEnv, runnerImport, transformWithOxc, defaultClientConditions, defaultServerConditions, defaultExternalConditions } from 'vite';
|
|
10
10
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
11
11
|
import { crawlFrameworkPkgs } from 'vitefu';
|
|
12
12
|
|
|
@@ -63,7 +63,16 @@ function webRequestFromNode(req, urlPath, res) {
|
|
|
63
63
|
signal = controller.signal;
|
|
64
64
|
}
|
|
65
65
|
const method = req.method || 'GET';
|
|
66
|
-
|
|
66
|
+
// Only attach a body when the request actually carries one. A web Request
|
|
67
|
+
// built by the browser for a bodyless POST has `body === null`, and the
|
|
68
|
+
// runtime keys off that (a present body that decodes to nothing is a 400
|
|
69
|
+
// since @solidjs/web 2.0.0-rc.5) — so an unconditionally attached (empty)
|
|
70
|
+
// stream misparses bodyless calls. HTTP/1 signals a body via
|
|
71
|
+
// Content-Length/Transfer-Encoding (RFC 9112 §6); the h2 compat API sets
|
|
72
|
+
// `stream.endAfterHeaders` when END_STREAM rode the headers frame.
|
|
73
|
+
const h2Stream = req.stream;
|
|
74
|
+
const hasBody = method !== 'GET' && method !== 'HEAD' && (h2Stream ? !h2Stream.endAfterHeaders : req.headers['transfer-encoding'] !== undefined || req.headers['content-length'] !== undefined && req.headers['content-length'] !== '0');
|
|
75
|
+
const body = hasBody ? Readable.toWeb(req) : undefined;
|
|
67
76
|
return new Request(url, {
|
|
68
77
|
method,
|
|
69
78
|
headers,
|
|
@@ -137,6 +146,75 @@ function joinBase(base, pathname) {
|
|
|
137
146
|
return (base.endsWith('/') ? base.slice(0, -1) : base) + pathname;
|
|
138
147
|
}
|
|
139
148
|
|
|
149
|
+
const TSRX_CSS_QUERY = '?solid-tsrx-css&lang.css';
|
|
150
|
+
const NULL_BYTE_PLACEHOLDER$1 = '/@id/__x00__';
|
|
151
|
+
function cleanModuleId(id) {
|
|
152
|
+
const query = id.indexOf('?');
|
|
153
|
+
return query === -1 ? id : id.slice(0, query);
|
|
154
|
+
}
|
|
155
|
+
function isTsrxModule(id) {
|
|
156
|
+
return cleanModuleId(id).toLowerCase().endsWith('.tsrx');
|
|
157
|
+
}
|
|
158
|
+
function isTsrxCssModule(id) {
|
|
159
|
+
const unwrapped = id.startsWith('\0') ? id.slice(1) : id.startsWith(NULL_BYTE_PLACEHOLDER$1) ? id.slice(NULL_BYTE_PLACEHOLDER$1.length) : id;
|
|
160
|
+
const queryIndex = unwrapped.indexOf('?');
|
|
161
|
+
if (queryIndex === -1 || !unwrapped.slice(0, queryIndex).toLowerCase().endsWith('.tsrx')) {
|
|
162
|
+
return false;
|
|
163
|
+
}
|
|
164
|
+
const params = unwrapped.slice(queryIndex + 1).split('&');
|
|
165
|
+
return params.includes('solid-tsrx-css') && params.includes('lang.css');
|
|
166
|
+
}
|
|
167
|
+
function resolveTsrxCssModule(id) {
|
|
168
|
+
if (!isTsrxCssModule(id)) return null;
|
|
169
|
+
if (id.startsWith('\0')) return id;
|
|
170
|
+
if (id.startsWith(NULL_BYTE_PLACEHOLDER$1)) {
|
|
171
|
+
return '\0' + id.slice(NULL_BYTE_PLACEHOLDER$1.length);
|
|
172
|
+
}
|
|
173
|
+
return '\0' + id;
|
|
174
|
+
}
|
|
175
|
+
function tsrxCssModuleId(id) {
|
|
176
|
+
return cleanModuleId(id) + TSRX_CSS_QUERY;
|
|
177
|
+
}
|
|
178
|
+
function resolvedTsrxCssModuleId(id) {
|
|
179
|
+
return '\0' + tsrxCssModuleId(id);
|
|
180
|
+
}
|
|
181
|
+
function tsrxCssSourceId(id) {
|
|
182
|
+
if (!id.startsWith('\0') || !isTsrxCssModule(id)) return null;
|
|
183
|
+
return cleanModuleId(id.slice(1));
|
|
184
|
+
}
|
|
185
|
+
function updateTsrxCss(cache, id, css) {
|
|
186
|
+
const key = cleanModuleId(id);
|
|
187
|
+
if (css) {
|
|
188
|
+
cache.set(key, css);
|
|
189
|
+
} else {
|
|
190
|
+
cache.delete(key);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
function prependTsrxCssImport(code, id) {
|
|
194
|
+
return `import ${JSON.stringify(tsrxCssModuleId(id))};\n${code}`;
|
|
195
|
+
}
|
|
196
|
+
function offsetSourceMapLine(map) {
|
|
197
|
+
if (map && typeof map === 'object' && 'mappings' in map && typeof map.mappings === 'string') {
|
|
198
|
+
return {
|
|
199
|
+
...map,
|
|
200
|
+
mappings: ';' + map.mappings
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (map && typeof map === 'object' && 'sections' in map && Array.isArray(map.sections)) {
|
|
204
|
+
return {
|
|
205
|
+
...map,
|
|
206
|
+
sections: map.sections.map(section => ({
|
|
207
|
+
...section,
|
|
208
|
+
offset: {
|
|
209
|
+
...section.offset,
|
|
210
|
+
line: section.offset.line + 1
|
|
211
|
+
}
|
|
212
|
+
}))
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
return map;
|
|
216
|
+
}
|
|
217
|
+
|
|
140
218
|
/**
|
|
141
219
|
* Dev-mode asset resolution: the `virtual:solid-manifest` module exports a
|
|
142
220
|
* resolver function in dev (instead of the static object a build produces),
|
|
@@ -246,6 +324,9 @@ const cssFileRegExp = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)$/;
|
|
|
246
324
|
// importer controls them — so they must not be SSR'd as style tags.
|
|
247
325
|
const nonAmbientQueryRegExp = /[?&](url|inline|raw)\b/;
|
|
248
326
|
const NULL_BYTE_PLACEHOLDER = '/@id/__x00__';
|
|
327
|
+
function isCssModuleUrl(url) {
|
|
328
|
+
return cssFileRegExp.test(url.split('?')[0]) || isTsrxCssModule(url);
|
|
329
|
+
}
|
|
249
330
|
|
|
250
331
|
// Per Vite's convention virtual module ids are prefixed with `\0`, which
|
|
251
332
|
// cannot appear in an HTML attribute (the parser replaces it). Serialize the
|
|
@@ -302,7 +383,7 @@ async function collectModuleDeps(env, file, deps, crawled, filter, onFile, impor
|
|
|
302
383
|
const node = await getModuleNode(env, file, importer);
|
|
303
384
|
if (!node?.id || deps.has(node)) return;
|
|
304
385
|
deps.add(node);
|
|
305
|
-
const isCss =
|
|
386
|
+
const isCss = isCssModuleUrl(node.url);
|
|
306
387
|
if (!isCss && node.file && !node.id.startsWith('\0') && !filter(node.file)) return;
|
|
307
388
|
if (node.file) onFile?.(node.file);
|
|
308
389
|
if (isCss) return;
|
|
@@ -334,8 +415,7 @@ async function collectDevStyleSources(env, files, onFile, filter = defaultStyleF
|
|
|
334
415
|
const seen = new Set();
|
|
335
416
|
for (const node of deps) {
|
|
336
417
|
if (!node.id) continue;
|
|
337
|
-
|
|
338
|
-
if (!cssFileRegExp.test(cleanUrl) || nonAmbientQueryRegExp.test(node.url)) continue;
|
|
418
|
+
if (!isCssModuleUrl(node.url) || nonAmbientQueryRegExp.test(node.url)) continue;
|
|
339
419
|
const id = wrapId(node.id);
|
|
340
420
|
if (seen.has(id)) continue;
|
|
341
421
|
seen.add(id);
|
|
@@ -549,7 +629,11 @@ function boundaryModules() {
|
|
|
549
629
|
}
|
|
550
630
|
|
|
551
631
|
/**
|
|
552
|
-
* Agent diagnostics surface (
|
|
632
|
+
* Agent diagnostics surface (dev serve only).
|
|
633
|
+
*
|
|
634
|
+
* Enabled automatically when the app declares `@solidjs/diagnostics` in
|
|
635
|
+
* its package.json (the `diagnostics` option overrides: `true` forces it
|
|
636
|
+
* on and errors if the package is missing, `false` opts out entirely).
|
|
553
637
|
*
|
|
554
638
|
* Three pieces:
|
|
555
639
|
* - an injected client module (virtual, imported by index.html or the
|
|
@@ -577,6 +661,36 @@ const METHODS = ['begin', 'end', 'active', 'whyDidRun', 'costs'];
|
|
|
577
661
|
|
|
578
662
|
/** How long the endpoint waits for a page to answer before failing the call. */
|
|
579
663
|
const RESPONSE_TIMEOUT_MS = 10_000;
|
|
664
|
+
|
|
665
|
+
/**
|
|
666
|
+
* Whether the app *declares* `@solidjs/diagnostics` — the auto-enable
|
|
667
|
+
* signal. Declaration in the nearest package.json (walking up from the
|
|
668
|
+
* Vite root, so a `client/` root still finds the app manifest) rather
|
|
669
|
+
* than node_modules presence: presence-based detection escapes the app
|
|
670
|
+
* into ancestor installs, which surprise-enables the surface for every
|
|
671
|
+
* fixture app inside a monorepo that happens to have the package
|
|
672
|
+
* somewhere above it (this broke the plugin's own example suites). A
|
|
673
|
+
* declared dependency is unambiguous intent, and resolution then works
|
|
674
|
+
* regardless of hoisting. `diagnostics: true` remains the override for
|
|
675
|
+
* setups the heuristic can't see.
|
|
676
|
+
*/
|
|
677
|
+
function detectDiagnosticsPackage(root) {
|
|
678
|
+
let dir = path.resolve(root);
|
|
679
|
+
while (true) {
|
|
680
|
+
const manifestPath = path.join(dir, 'package.json');
|
|
681
|
+
if (fs.existsSync(manifestPath)) {
|
|
682
|
+
try {
|
|
683
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
684
|
+
return !!(manifest.dependencies?.[DIAGNOSTICS_PACKAGE] ?? manifest.devDependencies?.[DIAGNOSTICS_PACKAGE] ?? manifest.optionalDependencies?.[DIAGNOSTICS_PACKAGE]);
|
|
685
|
+
} catch {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
const parent = path.dirname(dir);
|
|
690
|
+
if (parent === dir) return false;
|
|
691
|
+
dir = parent;
|
|
692
|
+
}
|
|
693
|
+
}
|
|
580
694
|
function diagnosticsClientModuleCode() {
|
|
581
695
|
// Runtime imports resolve to the APP's diagnostics package (see the
|
|
582
696
|
// resolveId assist below) — the page speaks its own package's protocol.
|
|
@@ -603,18 +717,26 @@ function readJsonBody(req) {
|
|
|
603
717
|
req.on('error', reject);
|
|
604
718
|
});
|
|
605
719
|
}
|
|
606
|
-
function solidDiagnostics() {
|
|
720
|
+
function solidDiagnostics(mode = 'auto') {
|
|
607
721
|
let root = process.cwd();
|
|
608
722
|
let base = '/';
|
|
723
|
+
// Resolved at configResolved: explicit `true` is unconditional (missing
|
|
724
|
+
// package becomes a hard error at bridge resolution); `'auto'` enables
|
|
725
|
+
// only when the app has the package installed.
|
|
726
|
+
let enabled = mode === true;
|
|
609
727
|
return {
|
|
610
728
|
name: 'solid:diagnostics',
|
|
611
729
|
// Dev-serve only: the channels this fronts exist in dev builds only.
|
|
730
|
+
// Test mode excluded — vitest (including browser mode) runs a dev
|
|
731
|
+
// serve, and injecting the bridge into test pages perturbs suites
|
|
732
|
+
// that never asked for it.
|
|
612
733
|
apply(_config, env) {
|
|
613
|
-
return env.command === 'serve' && !env.isPreview;
|
|
734
|
+
return env.command === 'serve' && !env.isPreview && env.mode !== 'test';
|
|
614
735
|
},
|
|
615
736
|
configResolved(config) {
|
|
616
737
|
root = config.root;
|
|
617
738
|
base = config.base;
|
|
739
|
+
if (mode === 'auto') enabled = detectDiagnosticsPackage(root);
|
|
618
740
|
},
|
|
619
741
|
async resolveId(source, importer) {
|
|
620
742
|
if (source === DIAGNOSTICS_CLIENT_ID) {
|
|
@@ -630,7 +752,7 @@ function solidDiagnostics() {
|
|
|
630
752
|
skipSelf: true
|
|
631
753
|
});
|
|
632
754
|
if (!resolved || resolved.id.startsWith('__vite-optional-peer-dep:')) {
|
|
633
|
-
this.error(`[@solidjs/vite-plugin] the diagnostics
|
|
755
|
+
this.error(`[@solidjs/vite-plugin] the diagnostics surface requires ${DIAGNOSTICS_PACKAGE} ` + 'installed in the app (it provides the in-page bridge). Install it as a ' + 'development dependency, or set `diagnostics: false` to opt out.');
|
|
634
756
|
}
|
|
635
757
|
return resolved;
|
|
636
758
|
}
|
|
@@ -643,6 +765,7 @@ function solidDiagnostics() {
|
|
|
643
765
|
// Plain (index.html) apps get the client module injected here;
|
|
644
766
|
// start-mode apps import it from the generated client entry instead.
|
|
645
767
|
transformIndexHtml() {
|
|
768
|
+
if (!enabled) return undefined;
|
|
646
769
|
return [{
|
|
647
770
|
tag: 'script',
|
|
648
771
|
attrs: {
|
|
@@ -653,6 +776,11 @@ function solidDiagnostics() {
|
|
|
653
776
|
}];
|
|
654
777
|
},
|
|
655
778
|
configureServer(server) {
|
|
779
|
+
// The whole surface (announcement, middleware, bridge injection) only
|
|
780
|
+
// exists when enabled, so the discovery breadcrumb never lies about
|
|
781
|
+
// a dead endpoint.
|
|
782
|
+
if (!enabled) return;
|
|
783
|
+
|
|
656
784
|
// Announce the surface in the startup block. This is a discovery
|
|
657
785
|
// channel: agents watching dev-server output learn the endpoint and
|
|
658
786
|
// the skill documents without any project-level pointer (AGENTS.md).
|
|
@@ -661,7 +789,7 @@ function solidDiagnostics() {
|
|
|
661
789
|
originalPrintUrls();
|
|
662
790
|
const local = server.resolvedUrls?.local[0];
|
|
663
791
|
const endpoint = local ? new URL(DIAGNOSTICS_ENDPOINT, local).href : DIAGNOSTICS_ENDPOINT;
|
|
664
|
-
server.config.logger.info(` ➜ Solid diagnostics: ${endpoint} ` + `(GET status; POST {"method":"begin"|"end"|"whyDidRun"|"costs"})
|
|
792
|
+
server.config.logger.info(` ➜ Solid diagnostics: ${endpoint} ` + `(GET status; POST {"method":"begin"|"end"|"whyDidRun"|"costs"})` + (mode === 'auto' ? ' — auto-enabled; `diagnostics: false` opts out' : '') + `\n ➜ Agent skills: node_modules/${DIAGNOSTICS_PACKAGE}/skills/agent-loops/SKILL.md, ` + `node_modules/solid-js/skills/reactivity-diagnostics/SKILL.md`);
|
|
665
793
|
};
|
|
666
794
|
const pending = new Map();
|
|
667
795
|
let nextId = 1;
|
|
@@ -672,6 +800,11 @@ function solidDiagnostics() {
|
|
|
672
800
|
clearTimeout(entry.timer);
|
|
673
801
|
entry.resolve(data);
|
|
674
802
|
});
|
|
803
|
+
|
|
804
|
+
// No host/origin validation here: on all supported Vite versions
|
|
805
|
+
// (peer range ^8) Vite's own DNS-rebinding host check runs ahead of
|
|
806
|
+
// plugin middleware — verified: requests with a disallowed Host
|
|
807
|
+
// header get Vite's 403 before reaching this handler.
|
|
675
808
|
server.middlewares.use(DIAGNOSTICS_ENDPOINT, async (req, res) => {
|
|
676
809
|
// The middleware mounts on the exact path; anything deeper is 404.
|
|
677
810
|
if (req.url && req.url !== '/' && req.url !== '') {
|
|
@@ -794,7 +927,7 @@ async function compile(id, code, options) {
|
|
|
794
927
|
mode: options.mode,
|
|
795
928
|
env: options.env,
|
|
796
929
|
directive: options.directive,
|
|
797
|
-
sourceMap:
|
|
930
|
+
sourceMap: options.sourceMap !== false,
|
|
798
931
|
register: options.definitions.register,
|
|
799
932
|
create: options.definitions.create
|
|
800
933
|
});
|
|
@@ -920,11 +1053,11 @@ function xxHash32(buffer, seed = 0) {
|
|
|
920
1053
|
* root — not the invocation directory — so running `vite` from outside the
|
|
921
1054
|
* project keeps compiling the same files. Absolute patterns are used as-is.
|
|
922
1055
|
*
|
|
923
|
-
* @default include "src/**\/*.{jsx,tsx,ts,js,mjs,cjs}", exclude "node_modules/**\/*.{jsx,tsx,ts,js,mjs,cjs}"
|
|
1056
|
+
* @default include "src/**\/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}", exclude "node_modules/**\/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}"
|
|
924
1057
|
*/
|
|
925
1058
|
|
|
926
|
-
const DEFAULT_INCLUDE = 'src/**/*.{jsx,tsx,ts,js,mjs,cjs}';
|
|
927
|
-
const DEFAULT_EXCLUDE = 'node_modules/**/*.{jsx,tsx,ts,js,mjs,cjs}';
|
|
1059
|
+
const DEFAULT_INCLUDE = 'src/**/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}';
|
|
1060
|
+
const DEFAULT_EXCLUDE = 'node_modules/**/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}';
|
|
928
1061
|
const DEFAULT_MANIFEST = 'virtual:solid-server-function-manifest';
|
|
929
1062
|
const DEFAULT_DIRECTIVE = 'use server';
|
|
930
1063
|
const DEFAULT_RUNTIME = '@solidjs/web/server-functions';
|
|
@@ -1167,9 +1300,11 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1167
1300
|
`export function handleServerFunctionRequest(request, options) {`, ` const { event: eventInit, ...rest } = options || {};`, ` return handle(request, {`, ` provideEvent: provideRequestEvent,`, ` ...(eventInit ? { createEvent: (req) => ({ request: req, locals: {}, ...eventInit }) } : {}),`, ` ...rest,`, ` });`, `}`].join('\n');
|
|
1168
1301
|
}
|
|
1169
1302
|
|
|
1170
|
-
// Function IDs are
|
|
1171
|
-
//
|
|
1172
|
-
//
|
|
1303
|
+
// Function IDs are `<name>-<xxHash32(root-relative path)>[-<ordinal>]`
|
|
1304
|
+
// (identity-keyed, solidjs/solid#3109). The name is a JS identifier and
|
|
1305
|
+
// never contains `-`, so the hash is always the second segment and maps
|
|
1306
|
+
// an incoming ID back to its module. Rebuilt whenever a transform has
|
|
1307
|
+
// grown the manifest.
|
|
1173
1308
|
const hashIndex = new Map();
|
|
1174
1309
|
let hashIndexSize = -1;
|
|
1175
1310
|
function moduleForFunctionId(functionId) {
|
|
@@ -1181,7 +1316,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1181
1316
|
}
|
|
1182
1317
|
hashIndexSize = manifest.server.size;
|
|
1183
1318
|
}
|
|
1184
|
-
return hashIndex.get(functionId.split('-'
|
|
1319
|
+
return hashIndex.get(functionId.split('-')[1]);
|
|
1185
1320
|
}
|
|
1186
1321
|
function moduleDevUrl(entry) {
|
|
1187
1322
|
const relative = path.relative(root, entry).split(path.sep).join('/');
|
|
@@ -1219,10 +1354,11 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1219
1354
|
if (internal.externalDevServer || !isRunnableEnvironment(ssrEnvironment)) {
|
|
1220
1355
|
return;
|
|
1221
1356
|
}
|
|
1222
|
-
// A call's address is `<endpoint>/<id>`
|
|
1223
|
-
//
|
|
1224
|
-
//
|
|
1225
|
-
//
|
|
1357
|
+
// A call's address is `<endpoint>/<id>` — plain HTTP — or
|
|
1358
|
+
// `<endpoint>/data/<id>` — the scripted transport's own path
|
|
1359
|
+
// (solidjs/solid#3076, #3094). Bare-mount requests still reach the
|
|
1360
|
+
// runtime handler (it answers 404), so misdirected posts fail
|
|
1361
|
+
// through the endpoint rather than falling through to SSR.
|
|
1226
1362
|
const underMount = (pathname, mount) => pathname === mount || pathname.startsWith(mount + '/');
|
|
1227
1363
|
server.middlewares.use((req, res, next) => {
|
|
1228
1364
|
const url = new URL(req.url || '/', 'http://localhost');
|
|
@@ -1241,9 +1377,15 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1241
1377
|
// Make sure the referenced module has been evaluated in the SSR
|
|
1242
1378
|
// environment so its registration exists — functions only client
|
|
1243
1379
|
// code references are never loaded by the SSR render itself.
|
|
1244
|
-
// The id lives in the path segment after the mount
|
|
1380
|
+
// The id lives in the path segment after the mount — behind a
|
|
1381
|
+
// literal `data` segment on the scripted transport's address
|
|
1382
|
+
// (solidjs/solid#3094). Segment count keeps the two apart: an id
|
|
1383
|
+
// occupies exactly one segment, so `data/<id>` is only ever a
|
|
1384
|
+
// data address, and a function id spelled `data` still parses at
|
|
1385
|
+
// the bare one.
|
|
1245
1386
|
const mount = basePrefixed ? resolvedEndpoint : endpoint;
|
|
1246
|
-
|
|
1387
|
+
let segment = url.pathname.slice(mount.length + 1);
|
|
1388
|
+
if (segment.startsWith('data/')) segment = segment.slice(5);
|
|
1247
1389
|
let functionId = null;
|
|
1248
1390
|
if (segment && !segment.includes('/')) {
|
|
1249
1391
|
try {
|
|
@@ -1252,14 +1394,6 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1252
1394
|
// not an address; the runtime handler answers the 404
|
|
1253
1395
|
}
|
|
1254
1396
|
}
|
|
1255
|
-
if (!functionId) {
|
|
1256
|
-
// TRANSITIONAL (remove before 3.0 stable): the retired header
|
|
1257
|
-
// and `?id=` addressing, kept only for the RC window where this
|
|
1258
|
-
// plugin meets a @solidjs/web older than the path-addressing
|
|
1259
|
-
// change (solidjs/solid#3076).
|
|
1260
|
-
const headerId = req.headers['x-server-function-id'];
|
|
1261
|
-
functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
|
|
1262
|
-
}
|
|
1263
1397
|
if (functionId) {
|
|
1264
1398
|
const entry = moduleForFunctionId(functionId);
|
|
1265
1399
|
if (entry) await ssrEnvironment.runner.import(moduleDevUrl(entry));
|
|
@@ -1288,6 +1422,46 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1288
1422
|
}
|
|
1289
1423
|
});
|
|
1290
1424
|
}
|
|
1425
|
+
async function transformModule(ctx, code, fileId, opts, tsrx) {
|
|
1426
|
+
const mode = getEnvironmentConsumer(ctx.environment, opts);
|
|
1427
|
+
const [id] = fileId.split('?');
|
|
1428
|
+
if (!id || !filter(id) || isTsrxModule(id) !== tsrx) return null;
|
|
1429
|
+
|
|
1430
|
+
// The directive has to appear literally, so anything without the
|
|
1431
|
+
// substring can skip the native parse entirely.
|
|
1432
|
+
if (!code.includes(directive)) return null;
|
|
1433
|
+
const result = await compile(id, code, {
|
|
1434
|
+
...(mode === 'server' ? serverOptions : clientOptions),
|
|
1435
|
+
mode,
|
|
1436
|
+
env,
|
|
1437
|
+
root,
|
|
1438
|
+
sourceMap: !tsrx || !!internal.tsrxSourceMap
|
|
1439
|
+
});
|
|
1440
|
+
if (!result.valid) return null;
|
|
1441
|
+
const preloader = preload[mode];
|
|
1442
|
+
if (preloader) preloader.defer();
|
|
1443
|
+
invalidateModules(currentServer, mergeManifestRecord(manifest.server, new Set([id])), manifestId);
|
|
1444
|
+
return {
|
|
1445
|
+
// Appended (not prepended) so the source map for the compiled module
|
|
1446
|
+
// stays valid; imports hoist and the endpoint is only read at call time.
|
|
1447
|
+
code: (result.code || '') + endpointConfigureSnippet(mode),
|
|
1448
|
+
map: result.map
|
|
1449
|
+
};
|
|
1450
|
+
}
|
|
1451
|
+
const compilerPlugin = {
|
|
1452
|
+
name: 'solid:server-functions/compiler',
|
|
1453
|
+
enforce: 'pre',
|
|
1454
|
+
transform(code, fileId, opts) {
|
|
1455
|
+
return transformModule(this, code, fileId, opts, false);
|
|
1456
|
+
}
|
|
1457
|
+
};
|
|
1458
|
+
const tsrxCompilerPlugin = {
|
|
1459
|
+
name: 'solid:server-functions/tsrx-compiler',
|
|
1460
|
+
enforce: 'pre',
|
|
1461
|
+
transform(code, fileId, opts) {
|
|
1462
|
+
return transformModule(this, code, fileId, opts, true);
|
|
1463
|
+
}
|
|
1464
|
+
};
|
|
1291
1465
|
return [{
|
|
1292
1466
|
name: 'solid:server-functions/setup',
|
|
1293
1467
|
enforce: 'pre',
|
|
@@ -1364,44 +1538,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1364
1538
|
}
|
|
1365
1539
|
return null;
|
|
1366
1540
|
}
|
|
1367
|
-
},
|
|
1368
|
-
name: 'solid:server-functions/compiler',
|
|
1369
|
-
enforce: 'pre',
|
|
1370
|
-
async transform(code, fileId, opts) {
|
|
1371
|
-
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1372
|
-
const [id] = fileId.split('?');
|
|
1373
|
-
if (!filter(id)) {
|
|
1374
|
-
return null;
|
|
1375
|
-
}
|
|
1376
|
-
|
|
1377
|
-
// Fast path: the directive has to appear literally, so anything
|
|
1378
|
-
// without the substring can skip the native parse entirely.
|
|
1379
|
-
if (!code.includes(directive)) {
|
|
1380
|
-
return null;
|
|
1381
|
-
}
|
|
1382
|
-
const result = await compile(id, code, {
|
|
1383
|
-
...(mode === 'server' ? serverOptions : clientOptions),
|
|
1384
|
-
mode,
|
|
1385
|
-
env,
|
|
1386
|
-
root
|
|
1387
|
-
});
|
|
1388
|
-
if (result.valid) {
|
|
1389
|
-
const preloader = preload[mode];
|
|
1390
|
-
if (preloader) {
|
|
1391
|
-
preloader.defer();
|
|
1392
|
-
}
|
|
1393
|
-
invalidateModules(currentServer, mergeManifestRecord(manifest.server, new Set([id])), manifestId);
|
|
1394
|
-
return {
|
|
1395
|
-
// Appended (not prepended) so the source map for the compiled
|
|
1396
|
-
// module stays valid; imports hoist and the endpoint is only
|
|
1397
|
-
// read at call time, never during module evaluation.
|
|
1398
|
-
code: (result.code || '') + endpointConfigureSnippet(mode),
|
|
1399
|
-
map: result.map
|
|
1400
|
-
};
|
|
1401
|
-
}
|
|
1402
|
-
return null;
|
|
1403
|
-
}
|
|
1404
|
-
}, ...startPlugins];
|
|
1541
|
+
}, compilerPlugin, ...(internal.tsrxAfterSolid ? [tsrxCompilerPlugin] : []), ...startPlugins];
|
|
1405
1542
|
}
|
|
1406
1543
|
|
|
1407
1544
|
const DEVTOOLS_PACKAGE = '@solidjs/start-devtools';
|
|
@@ -1496,9 +1633,9 @@ const ERROR_BOUNDARY_ID = 'virtual:solid-ssr-error-boundary.tsx';
|
|
|
1496
1633
|
const MANIFEST_ID = 'virtual:solid-manifest';
|
|
1497
1634
|
const SERVER_FUNCTION_HANDLER_ID = 'virtual:solid-server-function-handler';
|
|
1498
1635
|
const STORAGE_SOURCE = '@solidjs/web/storage';
|
|
1499
|
-
const ENTRY_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.mjs'];
|
|
1500
|
-
const APP_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js'];
|
|
1501
|
-
const DOCUMENT_EXTENSIONS = ['.tsx', '.jsx'];
|
|
1636
|
+
const ENTRY_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.mjs', '.tsrx'];
|
|
1637
|
+
const APP_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.tsrx'];
|
|
1638
|
+
const DOCUMENT_EXTENSIONS = ['.tsx', '.jsx', '.tsrx'];
|
|
1502
1639
|
function probe(root, stem, extensions) {
|
|
1503
1640
|
for (const ext of extensions) {
|
|
1504
1641
|
if (existsSync(path.resolve(root, stem + ext))) return stem + ext;
|
|
@@ -1606,7 +1743,9 @@ function startServe(options, internal = {}) {
|
|
|
1606
1743
|
const serverComponents = !!internal.serverComponents;
|
|
1607
1744
|
const errorBoundary = options.errorBoundary !== false;
|
|
1608
1745
|
const styleFilter = internal.styleFilter;
|
|
1609
|
-
|
|
1746
|
+
// `'auto'` resolves against the project root in configResolved, before
|
|
1747
|
+
// any of the (lazy) uses in entry codegen and the entry transform.
|
|
1748
|
+
let diagnostics = internal.diagnostics === true;
|
|
1610
1749
|
let devtoolsEnabled = false;
|
|
1611
1750
|
let devtoolsResolutions = {};
|
|
1612
1751
|
let devtoolsIds = {};
|
|
@@ -1898,7 +2037,8 @@ function startServe(options, internal = {}) {
|
|
|
1898
2037
|
lines.push(``, `async function dispatchRequest(request, event, options) {`);
|
|
1899
2038
|
if (composeServerFunctions) {
|
|
1900
2039
|
lines.push(
|
|
1901
|
-
// A call's address is `<endpoint>/<id>`
|
|
2040
|
+
// A call's address is `<endpoint>/<id>` or `<endpoint>/data/<id>`
|
|
2041
|
+
// (solidjs/solid#3076, #3094); the prefix gate covers both, and the
|
|
1902
2042
|
// bare mount still routes so a misaddressed request 404s through the
|
|
1903
2043
|
// runtime handler instead of rendering a page at it.
|
|
1904
2044
|
` const requestPath = new URL(request.url).pathname;`, ` if (requestPath === endpoint || requestPath.startsWith(endpoint + '/')) {`,
|
|
@@ -1971,6 +2111,7 @@ function startServe(options, internal = {}) {
|
|
|
1971
2111
|
devtoolsResolutions = {};
|
|
1972
2112
|
devtoolsIds = {};
|
|
1973
2113
|
entries = resolveEntries(root, options, clientMode);
|
|
2114
|
+
internal.onDocumentResolved?.(entries.document);
|
|
1974
2115
|
middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
|
|
1975
2116
|
// Server-mode only, like `entryServer`/`external` (a documented
|
|
1976
2117
|
// no-op in client mode so configs survive the `ssr` boolean flip).
|
|
@@ -2114,6 +2255,12 @@ function startServe(options, internal = {}) {
|
|
|
2114
2255
|
root = config.root;
|
|
2115
2256
|
base = config.base;
|
|
2116
2257
|
isBuild = config.command === 'build';
|
|
2258
|
+
// Test mode excluded for the same reason as the surface plugin's
|
|
2259
|
+
// `apply`: vitest runs a dev serve, and test pages should not get
|
|
2260
|
+
// the bridge import injected into their client entries.
|
|
2261
|
+
if (internal.diagnostics === 'auto' && !isBuild && config.mode !== 'test') {
|
|
2262
|
+
diagnostics = detectDiagnosticsPackage(root);
|
|
2263
|
+
}
|
|
2117
2264
|
},
|
|
2118
2265
|
resolveId(source, importer, opts) {
|
|
2119
2266
|
if (source === HANDLER_ID) {
|
|
@@ -2964,6 +3111,11 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
|
|
|
2964
3111
|
* solid-refresh#85 — is no longer used at all).
|
|
2965
3112
|
*/
|
|
2966
3113
|
const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
|
|
3114
|
+
|
|
3115
|
+
// Appended to the document shell's client compile instead of a refresh
|
|
3116
|
+
// boundary (see documentModuleId in solidPlugin): self-accept, then
|
|
3117
|
+
// invalidate — Vite's spelling for "this module cannot hot-update, reload".
|
|
3118
|
+
const DOCUMENT_HMR_DECLINE = '\nif (import.meta.hot) {\n import.meta.hot.accept(() => import.meta.hot.invalidate());\n}\n';
|
|
2967
3119
|
const DEFAULT_STYLE_EXCLUDE = /node_modules/;
|
|
2968
3120
|
const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
|
|
2969
3121
|
const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
|
|
@@ -3225,7 +3377,8 @@ function solidPlugin(options = {}) {
|
|
|
3225
3377
|
// resolve against the Vite root, not process.cwd() — running `vite` from
|
|
3226
3378
|
// outside the project would otherwise change what the filter matches.
|
|
3227
3379
|
let filter = createFilter(options.include, options.exclude);
|
|
3228
|
-
const
|
|
3380
|
+
const serverComponentsOption = typeof options.serverFunctions === 'object' ? options.serverFunctions.components : undefined;
|
|
3381
|
+
const serverComponents = !!serverComponentsOption;
|
|
3229
3382
|
// `start: true` is sugar for the empty options bag — one start mode,
|
|
3230
3383
|
// two spellings — so normalize here and let everything downstream see a
|
|
3231
3384
|
// single shape (`false` behaves exactly like omission).
|
|
@@ -3255,6 +3408,15 @@ function solidPlugin(options = {}) {
|
|
|
3255
3408
|
const externalDevServer = !!options.ssr && !!startOptions?.external;
|
|
3256
3409
|
let needHmr = false;
|
|
3257
3410
|
let replaceDev = false;
|
|
3411
|
+
// Resolved absolute path of the start-mode document shell (normalized to
|
|
3412
|
+
// forward slashes, matching Vite ids), reported back by the start plugin's
|
|
3413
|
+
// config hook. The document is the one module whose client compile must
|
|
3414
|
+
// decline HMR instead of taking a refresh boundary: it hydrates the whole
|
|
3415
|
+
// `document`, and no component swap can re-claim `document.documentElement`
|
|
3416
|
+
// — an accepted update would be absorbed with nothing visibly changing
|
|
3417
|
+
// (solidjs/solid#3151). Declining makes a save invalidate the module, so
|
|
3418
|
+
// Vite falls back to a full page reload: the honest cost.
|
|
3419
|
+
let documentModuleId = null;
|
|
3258
3420
|
// The live dev server, kept so the dev manifest module can bake the bridge
|
|
3259
3421
|
// endpoint URL in when its code is generated (see devManifestBridgeUrl).
|
|
3260
3422
|
let devServer = null;
|
|
@@ -3266,6 +3428,7 @@ function solidPlugin(options = {}) {
|
|
|
3266
3428
|
let base = '/';
|
|
3267
3429
|
let clientOutDir = null;
|
|
3268
3430
|
let solidPkgsConfig;
|
|
3431
|
+
const tsrxCss = new Map();
|
|
3269
3432
|
|
|
3270
3433
|
// The client build's manifest, read back by SSR builds. In builder-mode
|
|
3271
3434
|
// (single process, e.g. SolidStart's nitro plugin) the client build runs
|
|
@@ -3360,6 +3523,44 @@ function solidPlugin(options = {}) {
|
|
|
3360
3523
|
const relativeId = path.relative(projectRoot, file).split(path.sep).join('/') + query;
|
|
3361
3524
|
return code + `\nexport const $$moduleUrl = ${JSON.stringify(relativeId)};\n`;
|
|
3362
3525
|
}
|
|
3526
|
+
function nativeTsrxCss(result) {
|
|
3527
|
+
const css = result.css;
|
|
3528
|
+
return typeof css === 'string' ? css : '';
|
|
3529
|
+
}
|
|
3530
|
+
function babelTsrxCss(result) {
|
|
3531
|
+
const css = result.metadata?.css;
|
|
3532
|
+
return typeof css === 'string' ? css : '';
|
|
3533
|
+
}
|
|
3534
|
+
async function compileTsrxCss(source, id) {
|
|
3535
|
+
const solidOptions = getSolidOptions(options, false, replaceDev, isTestMode);
|
|
3536
|
+
if (options.compiler === 'babel') {
|
|
3537
|
+
const babelUserOptions = await getBabelUserOptions(options, source, id, false);
|
|
3538
|
+
const babelOptions = mergeAndConcat(babelUserOptions, {
|
|
3539
|
+
root: projectRoot,
|
|
3540
|
+
// Keep .tsrx: the Babel plugin uses it to select its TSRX parser.
|
|
3541
|
+
filename: id,
|
|
3542
|
+
sourceFileName: id,
|
|
3543
|
+
ast: false,
|
|
3544
|
+
code: false,
|
|
3545
|
+
sourceMaps: false,
|
|
3546
|
+
configFile: false,
|
|
3547
|
+
babelrc: false,
|
|
3548
|
+
parserOpts: {
|
|
3549
|
+
plugins: ['jsx', 'decorators', 'typescript']
|
|
3550
|
+
},
|
|
3551
|
+
plugins: [[solid, solidOptions]]
|
|
3552
|
+
});
|
|
3553
|
+
const result = await babel.transformAsync(source, babelOptions);
|
|
3554
|
+
return result ? babelTsrxCss(result) : '';
|
|
3555
|
+
}
|
|
3556
|
+
const compiler = await loadNativeCompiler();
|
|
3557
|
+
const result = await compiler.transformAsync(source, {
|
|
3558
|
+
...solidOptions,
|
|
3559
|
+
filename: id,
|
|
3560
|
+
sourceMap: false
|
|
3561
|
+
});
|
|
3562
|
+
return nativeTsrxCss(result);
|
|
3563
|
+
}
|
|
3363
3564
|
const mainPlugin = {
|
|
3364
3565
|
name: 'solid',
|
|
3365
3566
|
enforce: 'pre',
|
|
@@ -3450,6 +3651,7 @@ function solidPlugin(options = {}) {
|
|
|
3450
3651
|
dedupe: nestedDeps
|
|
3451
3652
|
},
|
|
3452
3653
|
optimizeDeps: {
|
|
3654
|
+
extensions: ['.tsrx'],
|
|
3453
3655
|
include: [...nestedDeps,
|
|
3454
3656
|
// Dev refresh wrappers import the solid-js/refresh runtime in
|
|
3455
3657
|
// every mode; pre-bundle it up front so its discovery doesn't
|
|
@@ -3470,7 +3672,28 @@ function solidPlugin(options = {}) {
|
|
|
3470
3672
|
jsx: {
|
|
3471
3673
|
runtime: 'classic'
|
|
3472
3674
|
}
|
|
3473
|
-
}
|
|
3675
|
+
},
|
|
3676
|
+
plugins: [{
|
|
3677
|
+
name: 'solid:tsrx-dep-scan',
|
|
3678
|
+
async transform(source, id) {
|
|
3679
|
+
if (!isTsrxModule(id) || isTsrxCssModule(id)) return null;
|
|
3680
|
+
const compiler = await loadNativeCompiler();
|
|
3681
|
+
const result = await compiler.transformAsync(source, {
|
|
3682
|
+
...getSolidOptions(options, false, replaceDev, isTestMode),
|
|
3683
|
+
filename: cleanModuleId(id),
|
|
3684
|
+
sourceMap: false
|
|
3685
|
+
});
|
|
3686
|
+
const stripped = await transformWithOxc(result.code, cleanModuleId(id) + '.tsx', {
|
|
3687
|
+
lang: 'tsx',
|
|
3688
|
+
sourcemap: false,
|
|
3689
|
+
target: 'esnext'
|
|
3690
|
+
});
|
|
3691
|
+
return {
|
|
3692
|
+
code: stripped.code,
|
|
3693
|
+
map: null
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
}]
|
|
3474
3697
|
}
|
|
3475
3698
|
},
|
|
3476
3699
|
...(Object.keys(test).length ? {
|
|
@@ -3529,8 +3752,13 @@ function solidPlugin(options = {}) {
|
|
|
3529
3752
|
resolve: projectRoot
|
|
3530
3753
|
});
|
|
3531
3754
|
styleFilter = createStyleFilter(projectRoot);
|
|
3532
|
-
|
|
3533
|
-
|
|
3755
|
+
// `components: 'external'` is the acknowledgement that a composing
|
|
3756
|
+
// host (e.g. the Astro adapter or TanStack Start's Solid integration)
|
|
3757
|
+
// owns the document wiring itself — behavior is identical to `true`,
|
|
3758
|
+
// only this warning is skipped. Under SSR start mode it's redundant
|
|
3759
|
+
// but harmless (treated exactly as `true`).
|
|
3760
|
+
if (serverComponents && serverComponentsOption !== 'external' && !(options.start && options.ssr)) {
|
|
3761
|
+
config.logger.warn('[@solidjs/vite-plugin] serverFunctions.components is set without SSR start mode (the `start` ' + 'option with `ssr: true`), so the plugin only installs the endpoint response transform ' + '(server functions returning components stream correctly). The document wiring — the ' + 'render plugin (with the direct-call transform) and the client-side ' + "installServerComponents() call — is emitted by SSR start mode's generated entries; " + 'without it, server components only mount from post-boot streams and your client code ' + 'must call installServerComponents() itself. If a composing host owns that wiring, set ' + "`components: 'external'` to acknowledge it and silence this warning.");
|
|
3534
3762
|
}
|
|
3535
3763
|
needHmr = config.command === 'serve' && config.mode !== 'production' && options.hot !== false && !options.refresh?.disabled;
|
|
3536
3764
|
},
|
|
@@ -3568,9 +3796,21 @@ function solidPlugin(options = {}) {
|
|
|
3568
3796
|
return origSend(...args);
|
|
3569
3797
|
};
|
|
3570
3798
|
},
|
|
3571
|
-
hotUpdate({
|
|
3572
|
-
|
|
3799
|
+
async hotUpdate({
|
|
3800
|
+
file,
|
|
3801
|
+
modules,
|
|
3802
|
+
read
|
|
3573
3803
|
}) {
|
|
3804
|
+
if (isTsrxModule(file) && this.environment.name === 'client') {
|
|
3805
|
+
updateTsrxCss(tsrxCss, file, await compileTsrxCss(await read(), file));
|
|
3806
|
+
const cssModule = this.environment.moduleGraph.getModuleById(resolvedTsrxCssModuleId(file));
|
|
3807
|
+
if (cssModule) {
|
|
3808
|
+
this.environment.moduleGraph.invalidateModule(cssModule);
|
|
3809
|
+
if (!modules.includes(cssModule)) modules = [...modules, cssModule];
|
|
3810
|
+
return modules;
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
|
|
3574
3814
|
// solid-refresh only injects HMR boundaries into client modules, so
|
|
3575
3815
|
// non-client environments have no accept handlers. Without this, Vite
|
|
3576
3816
|
// would see no boundaries and send full-reload messages that race with
|
|
@@ -3589,11 +3829,26 @@ function solidPlugin(options = {}) {
|
|
|
3589
3829
|
this.environment.hot.send({
|
|
3590
3830
|
type: 'full-reload'
|
|
3591
3831
|
});
|
|
3832
|
+
// Server-only modules are the exception to the suppression: a file
|
|
3833
|
+
// with no modules in the client graph has no browser HMR path at
|
|
3834
|
+
// all — nothing client-side accepts it, so staying silent leaves
|
|
3835
|
+
// the browser rendering stale server output until a manual refresh
|
|
3836
|
+
// (e.g. the document shell, which only the server ever imports;
|
|
3837
|
+
// solidjs/solid#3151). Reload the page: the honest cost, and there
|
|
3838
|
+
// is no client update to race with by construction.
|
|
3839
|
+
const clientEnv = devServer?.environments.client;
|
|
3840
|
+
if (clientEnv && !clientEnv.moduleGraph.getModulesByFile(file)?.size) {
|
|
3841
|
+
clientEnv.hot.send({
|
|
3842
|
+
type: 'full-reload'
|
|
3843
|
+
});
|
|
3844
|
+
}
|
|
3592
3845
|
}
|
|
3593
3846
|
return [];
|
|
3594
3847
|
}
|
|
3595
3848
|
},
|
|
3596
3849
|
resolveId(id) {
|
|
3850
|
+
const tsrxCssId = resolveTsrxCssModule(id);
|
|
3851
|
+
if (tsrxCssId) return tsrxCssId;
|
|
3597
3852
|
if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_VIRTUAL_MANIFEST_ID;
|
|
3598
3853
|
},
|
|
3599
3854
|
moduleParsed(info) {
|
|
@@ -3606,7 +3861,7 @@ function solidPlugin(options = {}) {
|
|
|
3606
3861
|
for (const depId of info.dynamicallyImportedIds || []) {
|
|
3607
3862
|
const cleanId = depId.split('?')[0];
|
|
3608
3863
|
if (/node_modules/.test(cleanId) || cleanId.startsWith('\0')) continue;
|
|
3609
|
-
if (
|
|
3864
|
+
if (!(/\.[mc]?[tj]sx?$/i.test(cleanId) || isTsrxModule(cleanId))) continue;
|
|
3610
3865
|
if (emittedLazyChunks.has(depId)) continue;
|
|
3611
3866
|
emittedLazyChunks.add(depId);
|
|
3612
3867
|
emittedLazyChunkRefs.push(this.emitFile({
|
|
@@ -3617,6 +3872,8 @@ function solidPlugin(options = {}) {
|
|
|
3617
3872
|
}
|
|
3618
3873
|
},
|
|
3619
3874
|
load(id) {
|
|
3875
|
+
const tsrxSource = tsrxCssSourceId(id);
|
|
3876
|
+
if (tsrxSource) return tsrxCss.get(tsrxSource) ?? '';
|
|
3620
3877
|
if (id === RESOLVED_VIRTUAL_MANIFEST_ID) {
|
|
3621
3878
|
if (!isBuild) {
|
|
3622
3879
|
return devManifestCode(projectRoot, base, devServer ? devManifestBridgeUrl(devServer) : null);
|
|
@@ -3658,6 +3915,7 @@ function solidPlugin(options = {}) {
|
|
|
3658
3915
|
}
|
|
3659
3916
|
},
|
|
3660
3917
|
async transform(source, id, transformOptions) {
|
|
3918
|
+
if (isTsrxCssModule(id)) return null;
|
|
3661
3919
|
const isSsr = getEnvironmentConsumer(this.environment, transformOptions) === 'server';
|
|
3662
3920
|
const currentFileExtension = getExtension(id);
|
|
3663
3921
|
const extensionsToWatch = options.extensions || [];
|
|
@@ -3673,14 +3931,15 @@ function solidPlugin(options = {}) {
|
|
|
3673
3931
|
// while the transform pipeline below works on the clean file path.
|
|
3674
3932
|
const moduleId = id;
|
|
3675
3933
|
id = id.replace(/\?.*$/, '');
|
|
3676
|
-
|
|
3934
|
+
const isTsrx = isTsrxModule(id);
|
|
3935
|
+
if (!(/\.[mc]?[tj]sx$/i.test(id) || isTsrx || allExtensions.includes(currentFileExtension))) {
|
|
3677
3936
|
return null;
|
|
3678
3937
|
}
|
|
3679
3938
|
const inNodeModules = /node_modules/.test(id);
|
|
3680
3939
|
const solidOptions = getSolidOptions(options, !!isSsr, replaceDev, isTestMode);
|
|
3681
3940
|
|
|
3682
3941
|
// We need to know if the current file extension has a typescript options tied to it
|
|
3683
|
-
const shouldBeProcessedWithTypescript = /\.[mc]?tsx$/i.test(id) || extensionsToWatch.some(extension => {
|
|
3942
|
+
const shouldBeProcessedWithTypescript = /\.[mc]?tsx$/i.test(id) || isTsrx || extensionsToWatch.some(extension => {
|
|
3684
3943
|
if (typeof extension === 'string') {
|
|
3685
3944
|
return extension.includes('tsx');
|
|
3686
3945
|
}
|
|
@@ -3692,14 +3951,19 @@ function solidPlugin(options = {}) {
|
|
|
3692
3951
|
if (shouldBeProcessedWithTypescript) {
|
|
3693
3952
|
plugins.push('typescript');
|
|
3694
3953
|
}
|
|
3695
|
-
|
|
3954
|
+
|
|
3955
|
+
// See the documentModuleId declaration: the document shell declines HMR
|
|
3956
|
+
// (no refresh boundary, explicit self-invalidation) so edits full-reload.
|
|
3957
|
+
const isDocumentShell = documentModuleId !== null && id === documentModuleId;
|
|
3958
|
+
const needRefresh = needHmr && !isSsr && !inNodeModules && !isDocumentShell;
|
|
3959
|
+
const declineHmr = isDocumentShell && needHmr && !isSsr;
|
|
3696
3960
|
const babelUserOptions = await getBabelUserOptions(options, source, id, !!isSsr);
|
|
3697
3961
|
|
|
3698
3962
|
// The native compiler picks its parser dialect from the file
|
|
3699
3963
|
// extension; custom extensions registered through `options.extensions`
|
|
3700
3964
|
// are unknown to it, so borrow a standard one matching the configured
|
|
3701
3965
|
// TypeScript-ness.
|
|
3702
|
-
const nativeFilename = /\.(?:[mc]?[jt]s|[jt]sx)$/i.test(id) ? id : id + (shouldBeProcessedWithTypescript ? '.tsx' : '.jsx');
|
|
3966
|
+
const nativeFilename = isTsrx || /\.(?:[mc]?[jt]s|[jt]sx)$/i.test(id) ? id : id + (shouldBeProcessedWithTypescript ? '.tsx' : '.jsx');
|
|
3703
3967
|
|
|
3704
3968
|
// Shared native prelude for every mode: the lazy() module-URL pass,
|
|
3705
3969
|
// then (dev/client/non-node_modules) the solid-refresh HMR pass, both
|
|
@@ -3709,6 +3973,97 @@ function solidPlugin(options = {}) {
|
|
|
3709
3973
|
const compiler = await loadNativeCompiler();
|
|
3710
3974
|
let code = source;
|
|
3711
3975
|
const maps = [];
|
|
3976
|
+
if (isTsrx) {
|
|
3977
|
+
// Solid lowering preserves authored TypeScript annotations; secondary
|
|
3978
|
+
// passes therefore parse the generated module as TSX even though no
|
|
3979
|
+
// template syntax remains.
|
|
3980
|
+
const generatedFilename = id + '.tsx';
|
|
3981
|
+
const babelBaseOptions = {
|
|
3982
|
+
root: projectRoot,
|
|
3983
|
+
filename: id,
|
|
3984
|
+
sourceFileName: id,
|
|
3985
|
+
ast: false,
|
|
3986
|
+
sourceMaps: true,
|
|
3987
|
+
configFile: false,
|
|
3988
|
+
babelrc: false,
|
|
3989
|
+
parserOpts: {
|
|
3990
|
+
plugins
|
|
3991
|
+
}
|
|
3992
|
+
};
|
|
3993
|
+
let css = '';
|
|
3994
|
+
if (options.compiler !== 'babel') {
|
|
3995
|
+
const result = await compiler.transformAsync(code, {
|
|
3996
|
+
...solidOptions,
|
|
3997
|
+
filename: id,
|
|
3998
|
+
sourceMap: true
|
|
3999
|
+
});
|
|
4000
|
+
code = result.code || '';
|
|
4001
|
+
css = nativeTsrxCss(result);
|
|
4002
|
+
maps.push(result.map);
|
|
4003
|
+
if (options.babel) {
|
|
4004
|
+
// The support pass cannot parse authored TSRX. On this route it
|
|
4005
|
+
// intentionally sees the lowered ordinary JavaScript instead.
|
|
4006
|
+
const supportOptions = mergeAndConcat(babelUserOptions, babelBaseOptions);
|
|
4007
|
+
// This pass sees native-lowered ordinary JavaScript, so do not
|
|
4008
|
+
// route it back through Babel's TSRX parser.
|
|
4009
|
+
supportOptions.filename = generatedFilename;
|
|
4010
|
+
const supportResult = await babel.transformAsync(code, supportOptions);
|
|
4011
|
+
if (!supportResult) return undefined;
|
|
4012
|
+
code = supportResult.code || '';
|
|
4013
|
+
maps.push(supportResult.map);
|
|
4014
|
+
}
|
|
4015
|
+
} else {
|
|
4016
|
+
const babelOptions = mergeAndConcat(babelUserOptions, {
|
|
4017
|
+
...babelBaseOptions,
|
|
4018
|
+
plugins: [[solid, solidOptions]]
|
|
4019
|
+
});
|
|
4020
|
+
const result = await babel.transformAsync(code, babelOptions);
|
|
4021
|
+
if (!result) return undefined;
|
|
4022
|
+
code = result.code || '';
|
|
4023
|
+
css = babelTsrxCss(result);
|
|
4024
|
+
maps.push(result.map);
|
|
4025
|
+
}
|
|
4026
|
+
const lazyResult = await compiler.transformLazyAsync(code, {
|
|
4027
|
+
filename: generatedFilename,
|
|
4028
|
+
sourceMap: true
|
|
4029
|
+
});
|
|
4030
|
+
code = lazyResult.code;
|
|
4031
|
+
maps.push(lazyResult.map);
|
|
4032
|
+
if (needRefresh) {
|
|
4033
|
+
const refreshResult = await compiler.transformRefreshAsync(code, {
|
|
4034
|
+
filename: generatedFilename,
|
|
4035
|
+
bundler: 'vite',
|
|
4036
|
+
fixRender: true,
|
|
4037
|
+
...(typeof options.refresh?.granular === 'boolean' ? {
|
|
4038
|
+
granular: options.refresh.granular
|
|
4039
|
+
} : {}),
|
|
4040
|
+
jsx: false,
|
|
4041
|
+
importSource: REFRESH_RUNTIME_SOURCE,
|
|
4042
|
+
sourceMap: true
|
|
4043
|
+
});
|
|
4044
|
+
code = refreshResult.code;
|
|
4045
|
+
maps.push(refreshResult.map);
|
|
4046
|
+
}
|
|
4047
|
+
code = injectSsrModuleId(await resolveLazyModuleUrls(this, code, id), moduleId, !!isSsr);
|
|
4048
|
+
let map = options.compiler === 'babel' ? combineSourcemaps(maps) : null;
|
|
4049
|
+
updateTsrxCss(tsrxCss, id, css);
|
|
4050
|
+
if (css) {
|
|
4051
|
+
code = prependTsrxCssImport(code, id);
|
|
4052
|
+
map = offsetSourceMapLine(map);
|
|
4053
|
+
}
|
|
4054
|
+
// Vite selects its TypeScript stripping by file extension. Since the
|
|
4055
|
+
// real module identity remains `.tsrx`, strip the annotations here
|
|
4056
|
+
// after Solid lowering instead of handing typed JavaScript to Rollup.
|
|
4057
|
+
const stripped = await transformWithOxc(code, generatedFilename, {
|
|
4058
|
+
lang: 'tsx',
|
|
4059
|
+
sourcemap: map != null,
|
|
4060
|
+
target: 'esnext'
|
|
4061
|
+
}, map ?? undefined);
|
|
4062
|
+
return {
|
|
4063
|
+
code: stripped.code,
|
|
4064
|
+
map: map == null ? null : stripped.map
|
|
4065
|
+
};
|
|
4066
|
+
}
|
|
3712
4067
|
const lazyResult = await compiler.transformLazyAsync(code, {
|
|
3713
4068
|
filename: nativeFilename,
|
|
3714
4069
|
sourceMap: true
|
|
@@ -3764,7 +4119,7 @@ function solidPlugin(options = {}) {
|
|
|
3764
4119
|
maps.push(result.map);
|
|
3765
4120
|
const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
|
|
3766
4121
|
return {
|
|
3767
|
-
code: finalCode,
|
|
4122
|
+
code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
|
|
3768
4123
|
map: combineSourcemaps(maps)
|
|
3769
4124
|
};
|
|
3770
4125
|
}
|
|
@@ -3785,26 +4140,30 @@ function solidPlugin(options = {}) {
|
|
|
3785
4140
|
maps.push(result.map);
|
|
3786
4141
|
const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
|
|
3787
4142
|
return {
|
|
3788
|
-
code: finalCode,
|
|
4143
|
+
code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
|
|
3789
4144
|
map: combineSourcemaps(maps)
|
|
3790
4145
|
};
|
|
3791
4146
|
}
|
|
3792
4147
|
};
|
|
3793
4148
|
|
|
3794
|
-
//
|
|
3795
|
-
//
|
|
3796
|
-
//
|
|
3797
|
-
//
|
|
3798
|
-
const
|
|
4149
|
+
// Ordinary modules need the directive transform before JSX. Authored TSRX
|
|
4150
|
+
// cannot be parsed by that standalone pass, so its companion compiler runs
|
|
4151
|
+
// after mainPlugin has lowered the file to ordinary JavaScript while keeping
|
|
4152
|
+
// the original .tsrx id for stable server-function hashes.
|
|
4153
|
+
const serverFunctionPlugins = options.serverFunctions ? serverFunctions(options.serverFunctions === true ? {} : options.serverFunctions, {
|
|
3799
4154
|
devMiddleware: true,
|
|
3800
4155
|
externalDevServer,
|
|
4156
|
+
tsrxAfterSolid: true,
|
|
4157
|
+
tsrxSourceMap: options.compiler === 'babel',
|
|
3801
4158
|
// With start mode on (either variant), the dev middleware dispatches
|
|
3802
4159
|
// the endpoint through the SSR handler so user middleware and the
|
|
3803
4160
|
// stub-backed request event front it exactly like page SSR.
|
|
3804
4161
|
...(startOptions ? {
|
|
3805
4162
|
ssrHandler: SSR_HANDLER_ID
|
|
3806
4163
|
} : {})
|
|
3807
|
-
})
|
|
4164
|
+
}) : [];
|
|
4165
|
+
const tsrxServerFunctionPlugin = serverFunctionPlugins.find(plugin => plugin.name === 'solid:server-functions/tsrx-compiler');
|
|
4166
|
+
const plugins = [boundaryModules(), ...serverFunctionPlugins.filter(plugin => plugin !== tsrxServerFunctionPlugin), mainPlugin, ...(tsrxServerFunctionPlugin ? [tsrxServerFunctionPlugin] : [])];
|
|
3808
4167
|
|
|
3809
4168
|
// The `start` option opts into start-mode serving on top of the transforms;
|
|
3810
4169
|
// the `ssr` boolean picks the mode (a bare `ssr: true` keeps the
|
|
@@ -3819,14 +4178,20 @@ function solidPlugin(options = {}) {
|
|
|
3819
4178
|
serverComponents,
|
|
3820
4179
|
ssr: !!options.ssr,
|
|
3821
4180
|
styleFilter: filterDevStyles,
|
|
3822
|
-
diagnostics:
|
|
4181
|
+
diagnostics: options.diagnostics ?? 'auto',
|
|
4182
|
+
onDocumentResolved(documentPath) {
|
|
4183
|
+
// Normalize to forward slashes to match Vite's transform ids.
|
|
4184
|
+
documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
|
|
4185
|
+
}
|
|
3823
4186
|
}));
|
|
3824
4187
|
}
|
|
3825
4188
|
|
|
3826
4189
|
// Agent diagnostics endpoint + injected bridge (dev serve only — the
|
|
3827
|
-
// plugin no-ops itself for builds and preview via `apply
|
|
3828
|
-
|
|
3829
|
-
|
|
4190
|
+
// plugin no-ops itself for builds and preview via `apply`, and in the
|
|
4191
|
+
// default auto mode additionally disables itself unless the app has
|
|
4192
|
+
// `@solidjs/diagnostics` installed).
|
|
4193
|
+
if (options.diagnostics !== false) {
|
|
4194
|
+
plugins.push(solidDiagnostics(options.diagnostics === true ? true : 'auto'));
|
|
3830
4195
|
}
|
|
3831
4196
|
|
|
3832
4197
|
// Builder-mode (environments API) client-before-server build ordering.
|