@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/cjs/index.cjs
CHANGED
|
@@ -87,7 +87,16 @@ function webRequestFromNode(req, urlPath, res) {
|
|
|
87
87
|
signal = controller.signal;
|
|
88
88
|
}
|
|
89
89
|
const method = req.method || 'GET';
|
|
90
|
-
|
|
90
|
+
// Only attach a body when the request actually carries one. A web Request
|
|
91
|
+
// built by the browser for a bodyless POST has `body === null`, and the
|
|
92
|
+
// runtime keys off that (a present body that decodes to nothing is a 400
|
|
93
|
+
// since @solidjs/web 2.0.0-rc.5) — so an unconditionally attached (empty)
|
|
94
|
+
// stream misparses bodyless calls. HTTP/1 signals a body via
|
|
95
|
+
// Content-Length/Transfer-Encoding (RFC 9112 §6); the h2 compat API sets
|
|
96
|
+
// `stream.endAfterHeaders` when END_STREAM rode the headers frame.
|
|
97
|
+
const h2Stream = req.stream;
|
|
98
|
+
const hasBody = method !== 'GET' && method !== 'HEAD' && (h2Stream ? !h2Stream.endAfterHeaders : req.headers['transfer-encoding'] !== undefined || req.headers['content-length'] !== undefined && req.headers['content-length'] !== '0');
|
|
99
|
+
const body = hasBody ? node_stream.Readable.toWeb(req) : undefined;
|
|
91
100
|
return new Request(url, {
|
|
92
101
|
method,
|
|
93
102
|
headers,
|
|
@@ -161,6 +170,75 @@ function joinBase(base, pathname) {
|
|
|
161
170
|
return (base.endsWith('/') ? base.slice(0, -1) : base) + pathname;
|
|
162
171
|
}
|
|
163
172
|
|
|
173
|
+
const TSRX_CSS_QUERY = '?solid-tsrx-css&lang.css';
|
|
174
|
+
const NULL_BYTE_PLACEHOLDER$1 = '/@id/__x00__';
|
|
175
|
+
function cleanModuleId(id) {
|
|
176
|
+
const query = id.indexOf('?');
|
|
177
|
+
return query === -1 ? id : id.slice(0, query);
|
|
178
|
+
}
|
|
179
|
+
function isTsrxModule(id) {
|
|
180
|
+
return cleanModuleId(id).toLowerCase().endsWith('.tsrx');
|
|
181
|
+
}
|
|
182
|
+
function isTsrxCssModule(id) {
|
|
183
|
+
const unwrapped = id.startsWith('\0') ? id.slice(1) : id.startsWith(NULL_BYTE_PLACEHOLDER$1) ? id.slice(NULL_BYTE_PLACEHOLDER$1.length) : id;
|
|
184
|
+
const queryIndex = unwrapped.indexOf('?');
|
|
185
|
+
if (queryIndex === -1 || !unwrapped.slice(0, queryIndex).toLowerCase().endsWith('.tsrx')) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
const params = unwrapped.slice(queryIndex + 1).split('&');
|
|
189
|
+
return params.includes('solid-tsrx-css') && params.includes('lang.css');
|
|
190
|
+
}
|
|
191
|
+
function resolveTsrxCssModule(id) {
|
|
192
|
+
if (!isTsrxCssModule(id)) return null;
|
|
193
|
+
if (id.startsWith('\0')) return id;
|
|
194
|
+
if (id.startsWith(NULL_BYTE_PLACEHOLDER$1)) {
|
|
195
|
+
return '\0' + id.slice(NULL_BYTE_PLACEHOLDER$1.length);
|
|
196
|
+
}
|
|
197
|
+
return '\0' + id;
|
|
198
|
+
}
|
|
199
|
+
function tsrxCssModuleId(id) {
|
|
200
|
+
return cleanModuleId(id) + TSRX_CSS_QUERY;
|
|
201
|
+
}
|
|
202
|
+
function resolvedTsrxCssModuleId(id) {
|
|
203
|
+
return '\0' + tsrxCssModuleId(id);
|
|
204
|
+
}
|
|
205
|
+
function tsrxCssSourceId(id) {
|
|
206
|
+
if (!id.startsWith('\0') || !isTsrxCssModule(id)) return null;
|
|
207
|
+
return cleanModuleId(id.slice(1));
|
|
208
|
+
}
|
|
209
|
+
function updateTsrxCss(cache, id, css) {
|
|
210
|
+
const key = cleanModuleId(id);
|
|
211
|
+
if (css) {
|
|
212
|
+
cache.set(key, css);
|
|
213
|
+
} else {
|
|
214
|
+
cache.delete(key);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function prependTsrxCssImport(code, id) {
|
|
218
|
+
return `import ${JSON.stringify(tsrxCssModuleId(id))};\n${code}`;
|
|
219
|
+
}
|
|
220
|
+
function offsetSourceMapLine(map) {
|
|
221
|
+
if (map && typeof map === 'object' && 'mappings' in map && typeof map.mappings === 'string') {
|
|
222
|
+
return {
|
|
223
|
+
...map,
|
|
224
|
+
mappings: ';' + map.mappings
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
if (map && typeof map === 'object' && 'sections' in map && Array.isArray(map.sections)) {
|
|
228
|
+
return {
|
|
229
|
+
...map,
|
|
230
|
+
sections: map.sections.map(section => ({
|
|
231
|
+
...section,
|
|
232
|
+
offset: {
|
|
233
|
+
...section.offset,
|
|
234
|
+
line: section.offset.line + 1
|
|
235
|
+
}
|
|
236
|
+
}))
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
return map;
|
|
240
|
+
}
|
|
241
|
+
|
|
164
242
|
/**
|
|
165
243
|
* Dev-mode asset resolution: the `virtual:solid-manifest` module exports a
|
|
166
244
|
* resolver function in dev (instead of the static object a build produces),
|
|
@@ -270,6 +348,9 @@ const cssFileRegExp = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)$/;
|
|
|
270
348
|
// importer controls them — so they must not be SSR'd as style tags.
|
|
271
349
|
const nonAmbientQueryRegExp = /[?&](url|inline|raw)\b/;
|
|
272
350
|
const NULL_BYTE_PLACEHOLDER = '/@id/__x00__';
|
|
351
|
+
function isCssModuleUrl(url) {
|
|
352
|
+
return cssFileRegExp.test(url.split('?')[0]) || isTsrxCssModule(url);
|
|
353
|
+
}
|
|
273
354
|
|
|
274
355
|
// Per Vite's convention virtual module ids are prefixed with `\0`, which
|
|
275
356
|
// cannot appear in an HTML attribute (the parser replaces it). Serialize the
|
|
@@ -326,7 +407,7 @@ async function collectModuleDeps(env, file, deps, crawled, filter, onFile, impor
|
|
|
326
407
|
const node = await getModuleNode(env, file, importer);
|
|
327
408
|
if (!node?.id || deps.has(node)) return;
|
|
328
409
|
deps.add(node);
|
|
329
|
-
const isCss =
|
|
410
|
+
const isCss = isCssModuleUrl(node.url);
|
|
330
411
|
if (!isCss && node.file && !node.id.startsWith('\0') && !filter(node.file)) return;
|
|
331
412
|
if (node.file) onFile?.(node.file);
|
|
332
413
|
if (isCss) return;
|
|
@@ -358,8 +439,7 @@ async function collectDevStyleSources(env, files, onFile, filter = defaultStyleF
|
|
|
358
439
|
const seen = new Set();
|
|
359
440
|
for (const node of deps) {
|
|
360
441
|
if (!node.id) continue;
|
|
361
|
-
|
|
362
|
-
if (!cssFileRegExp.test(cleanUrl) || nonAmbientQueryRegExp.test(node.url)) continue;
|
|
442
|
+
if (!isCssModuleUrl(node.url) || nonAmbientQueryRegExp.test(node.url)) continue;
|
|
363
443
|
const id = wrapId(node.id);
|
|
364
444
|
if (seen.has(id)) continue;
|
|
365
445
|
seen.add(id);
|
|
@@ -573,7 +653,11 @@ function boundaryModules() {
|
|
|
573
653
|
}
|
|
574
654
|
|
|
575
655
|
/**
|
|
576
|
-
* Agent diagnostics surface (
|
|
656
|
+
* Agent diagnostics surface (dev serve only).
|
|
657
|
+
*
|
|
658
|
+
* Enabled automatically when the app declares `@solidjs/diagnostics` in
|
|
659
|
+
* its package.json (the `diagnostics` option overrides: `true` forces it
|
|
660
|
+
* on and errors if the package is missing, `false` opts out entirely).
|
|
577
661
|
*
|
|
578
662
|
* Three pieces:
|
|
579
663
|
* - an injected client module (virtual, imported by index.html or the
|
|
@@ -601,6 +685,36 @@ const METHODS = ['begin', 'end', 'active', 'whyDidRun', 'costs'];
|
|
|
601
685
|
|
|
602
686
|
/** How long the endpoint waits for a page to answer before failing the call. */
|
|
603
687
|
const RESPONSE_TIMEOUT_MS = 10_000;
|
|
688
|
+
|
|
689
|
+
/**
|
|
690
|
+
* Whether the app *declares* `@solidjs/diagnostics` — the auto-enable
|
|
691
|
+
* signal. Declaration in the nearest package.json (walking up from the
|
|
692
|
+
* Vite root, so a `client/` root still finds the app manifest) rather
|
|
693
|
+
* than node_modules presence: presence-based detection escapes the app
|
|
694
|
+
* into ancestor installs, which surprise-enables the surface for every
|
|
695
|
+
* fixture app inside a monorepo that happens to have the package
|
|
696
|
+
* somewhere above it (this broke the plugin's own example suites). A
|
|
697
|
+
* declared dependency is unambiguous intent, and resolution then works
|
|
698
|
+
* regardless of hoisting. `diagnostics: true` remains the override for
|
|
699
|
+
* setups the heuristic can't see.
|
|
700
|
+
*/
|
|
701
|
+
function detectDiagnosticsPackage(root) {
|
|
702
|
+
let dir = path.resolve(root);
|
|
703
|
+
while (true) {
|
|
704
|
+
const manifestPath = path.join(dir, 'package.json');
|
|
705
|
+
if (fs.existsSync(manifestPath)) {
|
|
706
|
+
try {
|
|
707
|
+
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
708
|
+
return !!(manifest.dependencies?.[DIAGNOSTICS_PACKAGE] ?? manifest.devDependencies?.[DIAGNOSTICS_PACKAGE] ?? manifest.optionalDependencies?.[DIAGNOSTICS_PACKAGE]);
|
|
709
|
+
} catch {
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
const parent = path.dirname(dir);
|
|
714
|
+
if (parent === dir) return false;
|
|
715
|
+
dir = parent;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
604
718
|
function diagnosticsClientModuleCode() {
|
|
605
719
|
// Runtime imports resolve to the APP's diagnostics package (see the
|
|
606
720
|
// resolveId assist below) — the page speaks its own package's protocol.
|
|
@@ -627,18 +741,26 @@ function readJsonBody(req) {
|
|
|
627
741
|
req.on('error', reject);
|
|
628
742
|
});
|
|
629
743
|
}
|
|
630
|
-
function solidDiagnostics() {
|
|
744
|
+
function solidDiagnostics(mode = 'auto') {
|
|
631
745
|
let root = process.cwd();
|
|
632
746
|
let base = '/';
|
|
747
|
+
// Resolved at configResolved: explicit `true` is unconditional (missing
|
|
748
|
+
// package becomes a hard error at bridge resolution); `'auto'` enables
|
|
749
|
+
// only when the app has the package installed.
|
|
750
|
+
let enabled = mode === true;
|
|
633
751
|
return {
|
|
634
752
|
name: 'solid:diagnostics',
|
|
635
753
|
// Dev-serve only: the channels this fronts exist in dev builds only.
|
|
754
|
+
// Test mode excluded — vitest (including browser mode) runs a dev
|
|
755
|
+
// serve, and injecting the bridge into test pages perturbs suites
|
|
756
|
+
// that never asked for it.
|
|
636
757
|
apply(_config, env) {
|
|
637
|
-
return env.command === 'serve' && !env.isPreview;
|
|
758
|
+
return env.command === 'serve' && !env.isPreview && env.mode !== 'test';
|
|
638
759
|
},
|
|
639
760
|
configResolved(config) {
|
|
640
761
|
root = config.root;
|
|
641
762
|
base = config.base;
|
|
763
|
+
if (mode === 'auto') enabled = detectDiagnosticsPackage(root);
|
|
642
764
|
},
|
|
643
765
|
async resolveId(source, importer) {
|
|
644
766
|
if (source === DIAGNOSTICS_CLIENT_ID) {
|
|
@@ -654,7 +776,7 @@ function solidDiagnostics() {
|
|
|
654
776
|
skipSelf: true
|
|
655
777
|
});
|
|
656
778
|
if (!resolved || resolved.id.startsWith('__vite-optional-peer-dep:')) {
|
|
657
|
-
this.error(`[@solidjs/vite-plugin] the diagnostics
|
|
779
|
+
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.');
|
|
658
780
|
}
|
|
659
781
|
return resolved;
|
|
660
782
|
}
|
|
@@ -667,6 +789,7 @@ function solidDiagnostics() {
|
|
|
667
789
|
// Plain (index.html) apps get the client module injected here;
|
|
668
790
|
// start-mode apps import it from the generated client entry instead.
|
|
669
791
|
transformIndexHtml() {
|
|
792
|
+
if (!enabled) return undefined;
|
|
670
793
|
return [{
|
|
671
794
|
tag: 'script',
|
|
672
795
|
attrs: {
|
|
@@ -677,6 +800,11 @@ function solidDiagnostics() {
|
|
|
677
800
|
}];
|
|
678
801
|
},
|
|
679
802
|
configureServer(server) {
|
|
803
|
+
// The whole surface (announcement, middleware, bridge injection) only
|
|
804
|
+
// exists when enabled, so the discovery breadcrumb never lies about
|
|
805
|
+
// a dead endpoint.
|
|
806
|
+
if (!enabled) return;
|
|
807
|
+
|
|
680
808
|
// Announce the surface in the startup block. This is a discovery
|
|
681
809
|
// channel: agents watching dev-server output learn the endpoint and
|
|
682
810
|
// the skill documents without any project-level pointer (AGENTS.md).
|
|
@@ -685,7 +813,7 @@ function solidDiagnostics() {
|
|
|
685
813
|
originalPrintUrls();
|
|
686
814
|
const local = server.resolvedUrls?.local[0];
|
|
687
815
|
const endpoint = local ? new URL(DIAGNOSTICS_ENDPOINT, local).href : DIAGNOSTICS_ENDPOINT;
|
|
688
|
-
server.config.logger.info(` ➜ Solid diagnostics: ${endpoint} ` + `(GET status; POST {"method":"begin"|"end"|"whyDidRun"|"costs"})
|
|
816
|
+
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`);
|
|
689
817
|
};
|
|
690
818
|
const pending = new Map();
|
|
691
819
|
let nextId = 1;
|
|
@@ -696,6 +824,11 @@ function solidDiagnostics() {
|
|
|
696
824
|
clearTimeout(entry.timer);
|
|
697
825
|
entry.resolve(data);
|
|
698
826
|
});
|
|
827
|
+
|
|
828
|
+
// No host/origin validation here: on all supported Vite versions
|
|
829
|
+
// (peer range ^8) Vite's own DNS-rebinding host check runs ahead of
|
|
830
|
+
// plugin middleware — verified: requests with a disallowed Host
|
|
831
|
+
// header get Vite's 403 before reaching this handler.
|
|
699
832
|
server.middlewares.use(DIAGNOSTICS_ENDPOINT, async (req, res) => {
|
|
700
833
|
// The middleware mounts on the exact path; anything deeper is 404.
|
|
701
834
|
if (req.url && req.url !== '/' && req.url !== '') {
|
|
@@ -818,7 +951,7 @@ async function compile(id, code, options) {
|
|
|
818
951
|
mode: options.mode,
|
|
819
952
|
env: options.env,
|
|
820
953
|
directive: options.directive,
|
|
821
|
-
sourceMap:
|
|
954
|
+
sourceMap: options.sourceMap !== false,
|
|
822
955
|
register: options.definitions.register,
|
|
823
956
|
create: options.definitions.create
|
|
824
957
|
});
|
|
@@ -944,11 +1077,11 @@ function xxHash32(buffer, seed = 0) {
|
|
|
944
1077
|
* root — not the invocation directory — so running `vite` from outside the
|
|
945
1078
|
* project keeps compiling the same files. Absolute patterns are used as-is.
|
|
946
1079
|
*
|
|
947
|
-
* @default include "src/**\/*.{jsx,tsx,ts,js,mjs,cjs}", exclude "node_modules/**\/*.{jsx,tsx,ts,js,mjs,cjs}"
|
|
1080
|
+
* @default include "src/**\/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}", exclude "node_modules/**\/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}"
|
|
948
1081
|
*/
|
|
949
1082
|
|
|
950
|
-
const DEFAULT_INCLUDE = 'src/**/*.{jsx,tsx,ts,js,mjs,cjs}';
|
|
951
|
-
const DEFAULT_EXCLUDE = 'node_modules/**/*.{jsx,tsx,ts,js,mjs,cjs}';
|
|
1083
|
+
const DEFAULT_INCLUDE = 'src/**/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}';
|
|
1084
|
+
const DEFAULT_EXCLUDE = 'node_modules/**/*.{jsx,tsx,tsrx,ts,js,mjs,cjs}';
|
|
952
1085
|
const DEFAULT_MANIFEST = 'virtual:solid-server-function-manifest';
|
|
953
1086
|
const DEFAULT_DIRECTIVE = 'use server';
|
|
954
1087
|
const DEFAULT_RUNTIME = '@solidjs/web/server-functions';
|
|
@@ -1191,9 +1324,11 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1191
1324
|
`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');
|
|
1192
1325
|
}
|
|
1193
1326
|
|
|
1194
|
-
// Function IDs are
|
|
1195
|
-
//
|
|
1196
|
-
//
|
|
1327
|
+
// Function IDs are `<name>-<xxHash32(root-relative path)>[-<ordinal>]`
|
|
1328
|
+
// (identity-keyed, solidjs/solid#3109). The name is a JS identifier and
|
|
1329
|
+
// never contains `-`, so the hash is always the second segment and maps
|
|
1330
|
+
// an incoming ID back to its module. Rebuilt whenever a transform has
|
|
1331
|
+
// grown the manifest.
|
|
1197
1332
|
const hashIndex = new Map();
|
|
1198
1333
|
let hashIndexSize = -1;
|
|
1199
1334
|
function moduleForFunctionId(functionId) {
|
|
@@ -1205,7 +1340,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1205
1340
|
}
|
|
1206
1341
|
hashIndexSize = manifest.server.size;
|
|
1207
1342
|
}
|
|
1208
|
-
return hashIndex.get(functionId.split('-'
|
|
1343
|
+
return hashIndex.get(functionId.split('-')[1]);
|
|
1209
1344
|
}
|
|
1210
1345
|
function moduleDevUrl(entry) {
|
|
1211
1346
|
const relative = path.relative(root, entry).split(path.sep).join('/');
|
|
@@ -1243,10 +1378,11 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1243
1378
|
if (internal.externalDevServer || !isRunnableEnvironment(ssrEnvironment)) {
|
|
1244
1379
|
return;
|
|
1245
1380
|
}
|
|
1246
|
-
// A call's address is `<endpoint>/<id>`
|
|
1247
|
-
//
|
|
1248
|
-
//
|
|
1249
|
-
//
|
|
1381
|
+
// A call's address is `<endpoint>/<id>` — plain HTTP — or
|
|
1382
|
+
// `<endpoint>/data/<id>` — the scripted transport's own path
|
|
1383
|
+
// (solidjs/solid#3076, #3094). Bare-mount requests still reach the
|
|
1384
|
+
// runtime handler (it answers 404), so misdirected posts fail
|
|
1385
|
+
// through the endpoint rather than falling through to SSR.
|
|
1250
1386
|
const underMount = (pathname, mount) => pathname === mount || pathname.startsWith(mount + '/');
|
|
1251
1387
|
server.middlewares.use((req, res, next) => {
|
|
1252
1388
|
const url = new URL(req.url || '/', 'http://localhost');
|
|
@@ -1265,9 +1401,15 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1265
1401
|
// Make sure the referenced module has been evaluated in the SSR
|
|
1266
1402
|
// environment so its registration exists — functions only client
|
|
1267
1403
|
// code references are never loaded by the SSR render itself.
|
|
1268
|
-
// The id lives in the path segment after the mount
|
|
1404
|
+
// The id lives in the path segment after the mount — behind a
|
|
1405
|
+
// literal `data` segment on the scripted transport's address
|
|
1406
|
+
// (solidjs/solid#3094). Segment count keeps the two apart: an id
|
|
1407
|
+
// occupies exactly one segment, so `data/<id>` is only ever a
|
|
1408
|
+
// data address, and a function id spelled `data` still parses at
|
|
1409
|
+
// the bare one.
|
|
1269
1410
|
const mount = basePrefixed ? resolvedEndpoint : endpoint;
|
|
1270
|
-
|
|
1411
|
+
let segment = url.pathname.slice(mount.length + 1);
|
|
1412
|
+
if (segment.startsWith('data/')) segment = segment.slice(5);
|
|
1271
1413
|
let functionId = null;
|
|
1272
1414
|
if (segment && !segment.includes('/')) {
|
|
1273
1415
|
try {
|
|
@@ -1276,14 +1418,6 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1276
1418
|
// not an address; the runtime handler answers the 404
|
|
1277
1419
|
}
|
|
1278
1420
|
}
|
|
1279
|
-
if (!functionId) {
|
|
1280
|
-
// TRANSITIONAL (remove before 3.0 stable): the retired header
|
|
1281
|
-
// and `?id=` addressing, kept only for the RC window where this
|
|
1282
|
-
// plugin meets a @solidjs/web older than the path-addressing
|
|
1283
|
-
// change (solidjs/solid#3076).
|
|
1284
|
-
const headerId = req.headers['x-server-function-id'];
|
|
1285
|
-
functionId = (typeof headerId === 'string' ? headerId.split('#')[0] : undefined) || url.searchParams.get('id');
|
|
1286
|
-
}
|
|
1287
1421
|
if (functionId) {
|
|
1288
1422
|
const entry = moduleForFunctionId(functionId);
|
|
1289
1423
|
if (entry) await ssrEnvironment.runner.import(moduleDevUrl(entry));
|
|
@@ -1312,6 +1446,46 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1312
1446
|
}
|
|
1313
1447
|
});
|
|
1314
1448
|
}
|
|
1449
|
+
async function transformModule(ctx, code, fileId, opts, tsrx) {
|
|
1450
|
+
const mode = getEnvironmentConsumer(ctx.environment, opts);
|
|
1451
|
+
const [id] = fileId.split('?');
|
|
1452
|
+
if (!id || !filter(id) || isTsrxModule(id) !== tsrx) return null;
|
|
1453
|
+
|
|
1454
|
+
// The directive has to appear literally, so anything without the
|
|
1455
|
+
// substring can skip the native parse entirely.
|
|
1456
|
+
if (!code.includes(directive)) return null;
|
|
1457
|
+
const result = await compile(id, code, {
|
|
1458
|
+
...(mode === 'server' ? serverOptions : clientOptions),
|
|
1459
|
+
mode,
|
|
1460
|
+
env,
|
|
1461
|
+
root,
|
|
1462
|
+
sourceMap: !tsrx || !!internal.tsrxSourceMap
|
|
1463
|
+
});
|
|
1464
|
+
if (!result.valid) return null;
|
|
1465
|
+
const preloader = preload[mode];
|
|
1466
|
+
if (preloader) preloader.defer();
|
|
1467
|
+
invalidateModules(currentServer, mergeManifestRecord(manifest.server, new Set([id])), manifestId);
|
|
1468
|
+
return {
|
|
1469
|
+
// Appended (not prepended) so the source map for the compiled module
|
|
1470
|
+
// stays valid; imports hoist and the endpoint is only read at call time.
|
|
1471
|
+
code: (result.code || '') + endpointConfigureSnippet(mode),
|
|
1472
|
+
map: result.map
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
const compilerPlugin = {
|
|
1476
|
+
name: 'solid:server-functions/compiler',
|
|
1477
|
+
enforce: 'pre',
|
|
1478
|
+
transform(code, fileId, opts) {
|
|
1479
|
+
return transformModule(this, code, fileId, opts, false);
|
|
1480
|
+
}
|
|
1481
|
+
};
|
|
1482
|
+
const tsrxCompilerPlugin = {
|
|
1483
|
+
name: 'solid:server-functions/tsrx-compiler',
|
|
1484
|
+
enforce: 'pre',
|
|
1485
|
+
transform(code, fileId, opts) {
|
|
1486
|
+
return transformModule(this, code, fileId, opts, true);
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1315
1489
|
return [{
|
|
1316
1490
|
name: 'solid:server-functions/setup',
|
|
1317
1491
|
enforce: 'pre',
|
|
@@ -1388,44 +1562,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1388
1562
|
}
|
|
1389
1563
|
return null;
|
|
1390
1564
|
}
|
|
1391
|
-
},
|
|
1392
|
-
name: 'solid:server-functions/compiler',
|
|
1393
|
-
enforce: 'pre',
|
|
1394
|
-
async transform(code, fileId, opts) {
|
|
1395
|
-
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1396
|
-
const [id] = fileId.split('?');
|
|
1397
|
-
if (!filter(id)) {
|
|
1398
|
-
return null;
|
|
1399
|
-
}
|
|
1400
|
-
|
|
1401
|
-
// Fast path: the directive has to appear literally, so anything
|
|
1402
|
-
// without the substring can skip the native parse entirely.
|
|
1403
|
-
if (!code.includes(directive)) {
|
|
1404
|
-
return null;
|
|
1405
|
-
}
|
|
1406
|
-
const result = await compile(id, code, {
|
|
1407
|
-
...(mode === 'server' ? serverOptions : clientOptions),
|
|
1408
|
-
mode,
|
|
1409
|
-
env,
|
|
1410
|
-
root
|
|
1411
|
-
});
|
|
1412
|
-
if (result.valid) {
|
|
1413
|
-
const preloader = preload[mode];
|
|
1414
|
-
if (preloader) {
|
|
1415
|
-
preloader.defer();
|
|
1416
|
-
}
|
|
1417
|
-
invalidateModules(currentServer, mergeManifestRecord(manifest.server, new Set([id])), manifestId);
|
|
1418
|
-
return {
|
|
1419
|
-
// Appended (not prepended) so the source map for the compiled
|
|
1420
|
-
// module stays valid; imports hoist and the endpoint is only
|
|
1421
|
-
// read at call time, never during module evaluation.
|
|
1422
|
-
code: (result.code || '') + endpointConfigureSnippet(mode),
|
|
1423
|
-
map: result.map
|
|
1424
|
-
};
|
|
1425
|
-
}
|
|
1426
|
-
return null;
|
|
1427
|
-
}
|
|
1428
|
-
}, ...startPlugins];
|
|
1565
|
+
}, compilerPlugin, ...(internal.tsrxAfterSolid ? [tsrxCompilerPlugin] : []), ...startPlugins];
|
|
1429
1566
|
}
|
|
1430
1567
|
|
|
1431
1568
|
const DEVTOOLS_PACKAGE = '@solidjs/start-devtools';
|
|
@@ -1520,9 +1657,9 @@ const ERROR_BOUNDARY_ID = 'virtual:solid-ssr-error-boundary.tsx';
|
|
|
1520
1657
|
const MANIFEST_ID = 'virtual:solid-manifest';
|
|
1521
1658
|
const SERVER_FUNCTION_HANDLER_ID = 'virtual:solid-server-function-handler';
|
|
1522
1659
|
const STORAGE_SOURCE = '@solidjs/web/storage';
|
|
1523
|
-
const ENTRY_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.mjs'];
|
|
1524
|
-
const APP_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js'];
|
|
1525
|
-
const DOCUMENT_EXTENSIONS = ['.tsx', '.jsx'];
|
|
1660
|
+
const ENTRY_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.mjs', '.tsrx'];
|
|
1661
|
+
const APP_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.tsrx'];
|
|
1662
|
+
const DOCUMENT_EXTENSIONS = ['.tsx', '.jsx', '.tsrx'];
|
|
1526
1663
|
function probe(root, stem, extensions) {
|
|
1527
1664
|
for (const ext of extensions) {
|
|
1528
1665
|
if (fs.existsSync(path.resolve(root, stem + ext))) return stem + ext;
|
|
@@ -1630,7 +1767,9 @@ function startServe(options, internal = {}) {
|
|
|
1630
1767
|
const serverComponents = !!internal.serverComponents;
|
|
1631
1768
|
const errorBoundary = options.errorBoundary !== false;
|
|
1632
1769
|
const styleFilter = internal.styleFilter;
|
|
1633
|
-
|
|
1770
|
+
// `'auto'` resolves against the project root in configResolved, before
|
|
1771
|
+
// any of the (lazy) uses in entry codegen and the entry transform.
|
|
1772
|
+
let diagnostics = internal.diagnostics === true;
|
|
1634
1773
|
let devtoolsEnabled = false;
|
|
1635
1774
|
let devtoolsResolutions = {};
|
|
1636
1775
|
let devtoolsIds = {};
|
|
@@ -1922,7 +2061,8 @@ function startServe(options, internal = {}) {
|
|
|
1922
2061
|
lines.push(``, `async function dispatchRequest(request, event, options) {`);
|
|
1923
2062
|
if (composeServerFunctions) {
|
|
1924
2063
|
lines.push(
|
|
1925
|
-
// A call's address is `<endpoint>/<id>`
|
|
2064
|
+
// A call's address is `<endpoint>/<id>` or `<endpoint>/data/<id>`
|
|
2065
|
+
// (solidjs/solid#3076, #3094); the prefix gate covers both, and the
|
|
1926
2066
|
// bare mount still routes so a misaddressed request 404s through the
|
|
1927
2067
|
// runtime handler instead of rendering a page at it.
|
|
1928
2068
|
` const requestPath = new URL(request.url).pathname;`, ` if (requestPath === endpoint || requestPath.startsWith(endpoint + '/')) {`,
|
|
@@ -1995,6 +2135,7 @@ function startServe(options, internal = {}) {
|
|
|
1995
2135
|
devtoolsResolutions = {};
|
|
1996
2136
|
devtoolsIds = {};
|
|
1997
2137
|
entries = resolveEntries(root, options, clientMode);
|
|
2138
|
+
internal.onDocumentResolved?.(entries.document);
|
|
1998
2139
|
middlewarePath = options.middleware ? path.resolve(root, normalizeUserPath(root, options.middleware, 'middleware')) : null;
|
|
1999
2140
|
// Server-mode only, like `entryServer`/`external` (a documented
|
|
2000
2141
|
// no-op in client mode so configs survive the `ssr` boolean flip).
|
|
@@ -2138,6 +2279,12 @@ function startServe(options, internal = {}) {
|
|
|
2138
2279
|
root = config.root;
|
|
2139
2280
|
base = config.base;
|
|
2140
2281
|
isBuild = config.command === 'build';
|
|
2282
|
+
// Test mode excluded for the same reason as the surface plugin's
|
|
2283
|
+
// `apply`: vitest runs a dev serve, and test pages should not get
|
|
2284
|
+
// the bridge import injected into their client entries.
|
|
2285
|
+
if (internal.diagnostics === 'auto' && !isBuild && config.mode !== 'test') {
|
|
2286
|
+
diagnostics = detectDiagnosticsPackage(root);
|
|
2287
|
+
}
|
|
2141
2288
|
},
|
|
2142
2289
|
resolveId(source, importer, opts) {
|
|
2143
2290
|
if (source === HANDLER_ID) {
|
|
@@ -2988,6 +3135,11 @@ const LAZY_PLACEHOLDER_PREFIX = '__SOLID_LAZY_MODULE__:';
|
|
|
2988
3135
|
* solid-refresh#85 — is no longer used at all).
|
|
2989
3136
|
*/
|
|
2990
3137
|
const REFRESH_RUNTIME_SOURCE = 'solid-js/refresh';
|
|
3138
|
+
|
|
3139
|
+
// Appended to the document shell's client compile instead of a refresh
|
|
3140
|
+
// boundary (see documentModuleId in solidPlugin): self-accept, then
|
|
3141
|
+
// invalidate — Vite's spelling for "this module cannot hot-update, reload".
|
|
3142
|
+
const DOCUMENT_HMR_DECLINE = '\nif (import.meta.hot) {\n import.meta.hot.accept(() => import.meta.hot.invalidate());\n}\n';
|
|
2991
3143
|
const DEFAULT_STYLE_EXCLUDE = /node_modules/;
|
|
2992
3144
|
const VIRTUAL_MANIFEST_ID = 'virtual:solid-manifest';
|
|
2993
3145
|
const RESOLVED_VIRTUAL_MANIFEST_ID = '\0' + VIRTUAL_MANIFEST_ID;
|
|
@@ -3249,7 +3401,8 @@ function solidPlugin(options = {}) {
|
|
|
3249
3401
|
// resolve against the Vite root, not process.cwd() — running `vite` from
|
|
3250
3402
|
// outside the project would otherwise change what the filter matches.
|
|
3251
3403
|
let filter = vite.createFilter(options.include, options.exclude);
|
|
3252
|
-
const
|
|
3404
|
+
const serverComponentsOption = typeof options.serverFunctions === 'object' ? options.serverFunctions.components : undefined;
|
|
3405
|
+
const serverComponents = !!serverComponentsOption;
|
|
3253
3406
|
// `start: true` is sugar for the empty options bag — one start mode,
|
|
3254
3407
|
// two spellings — so normalize here and let everything downstream see a
|
|
3255
3408
|
// single shape (`false` behaves exactly like omission).
|
|
@@ -3279,6 +3432,15 @@ function solidPlugin(options = {}) {
|
|
|
3279
3432
|
const externalDevServer = !!options.ssr && !!startOptions?.external;
|
|
3280
3433
|
let needHmr = false;
|
|
3281
3434
|
let replaceDev = false;
|
|
3435
|
+
// Resolved absolute path of the start-mode document shell (normalized to
|
|
3436
|
+
// forward slashes, matching Vite ids), reported back by the start plugin's
|
|
3437
|
+
// config hook. The document is the one module whose client compile must
|
|
3438
|
+
// decline HMR instead of taking a refresh boundary: it hydrates the whole
|
|
3439
|
+
// `document`, and no component swap can re-claim `document.documentElement`
|
|
3440
|
+
// — an accepted update would be absorbed with nothing visibly changing
|
|
3441
|
+
// (solidjs/solid#3151). Declining makes a save invalidate the module, so
|
|
3442
|
+
// Vite falls back to a full page reload: the honest cost.
|
|
3443
|
+
let documentModuleId = null;
|
|
3282
3444
|
// The live dev server, kept so the dev manifest module can bake the bridge
|
|
3283
3445
|
// endpoint URL in when its code is generated (see devManifestBridgeUrl).
|
|
3284
3446
|
let devServer = null;
|
|
@@ -3290,6 +3452,7 @@ function solidPlugin(options = {}) {
|
|
|
3290
3452
|
let base = '/';
|
|
3291
3453
|
let clientOutDir = null;
|
|
3292
3454
|
let solidPkgsConfig;
|
|
3455
|
+
const tsrxCss = new Map();
|
|
3293
3456
|
|
|
3294
3457
|
// The client build's manifest, read back by SSR builds. In builder-mode
|
|
3295
3458
|
// (single process, e.g. SolidStart's nitro plugin) the client build runs
|
|
@@ -3384,6 +3547,44 @@ function solidPlugin(options = {}) {
|
|
|
3384
3547
|
const relativeId = path.relative(projectRoot, file).split(path.sep).join('/') + query;
|
|
3385
3548
|
return code + `\nexport const $$moduleUrl = ${JSON.stringify(relativeId)};\n`;
|
|
3386
3549
|
}
|
|
3550
|
+
function nativeTsrxCss(result) {
|
|
3551
|
+
const css = result.css;
|
|
3552
|
+
return typeof css === 'string' ? css : '';
|
|
3553
|
+
}
|
|
3554
|
+
function babelTsrxCss(result) {
|
|
3555
|
+
const css = result.metadata?.css;
|
|
3556
|
+
return typeof css === 'string' ? css : '';
|
|
3557
|
+
}
|
|
3558
|
+
async function compileTsrxCss(source, id) {
|
|
3559
|
+
const solidOptions = getSolidOptions(options, false, replaceDev, isTestMode);
|
|
3560
|
+
if (options.compiler === 'babel') {
|
|
3561
|
+
const babelUserOptions = await getBabelUserOptions(options, source, id, false);
|
|
3562
|
+
const babelOptions = mergeAnything.mergeAndConcat(babelUserOptions, {
|
|
3563
|
+
root: projectRoot,
|
|
3564
|
+
// Keep .tsrx: the Babel plugin uses it to select its TSRX parser.
|
|
3565
|
+
filename: id,
|
|
3566
|
+
sourceFileName: id,
|
|
3567
|
+
ast: false,
|
|
3568
|
+
code: false,
|
|
3569
|
+
sourceMaps: false,
|
|
3570
|
+
configFile: false,
|
|
3571
|
+
babelrc: false,
|
|
3572
|
+
parserOpts: {
|
|
3573
|
+
plugins: ['jsx', 'decorators', 'typescript']
|
|
3574
|
+
},
|
|
3575
|
+
plugins: [[solid, solidOptions]]
|
|
3576
|
+
});
|
|
3577
|
+
const result = await babel__namespace.transformAsync(source, babelOptions);
|
|
3578
|
+
return result ? babelTsrxCss(result) : '';
|
|
3579
|
+
}
|
|
3580
|
+
const compiler = await loadNativeCompiler();
|
|
3581
|
+
const result = await compiler.transformAsync(source, {
|
|
3582
|
+
...solidOptions,
|
|
3583
|
+
filename: id,
|
|
3584
|
+
sourceMap: false
|
|
3585
|
+
});
|
|
3586
|
+
return nativeTsrxCss(result);
|
|
3587
|
+
}
|
|
3387
3588
|
const mainPlugin = {
|
|
3388
3589
|
name: 'solid',
|
|
3389
3590
|
enforce: 'pre',
|
|
@@ -3474,6 +3675,7 @@ function solidPlugin(options = {}) {
|
|
|
3474
3675
|
dedupe: nestedDeps
|
|
3475
3676
|
},
|
|
3476
3677
|
optimizeDeps: {
|
|
3678
|
+
extensions: ['.tsrx'],
|
|
3477
3679
|
include: [...nestedDeps,
|
|
3478
3680
|
// Dev refresh wrappers import the solid-js/refresh runtime in
|
|
3479
3681
|
// every mode; pre-bundle it up front so its discovery doesn't
|
|
@@ -3494,7 +3696,28 @@ function solidPlugin(options = {}) {
|
|
|
3494
3696
|
jsx: {
|
|
3495
3697
|
runtime: 'classic'
|
|
3496
3698
|
}
|
|
3497
|
-
}
|
|
3699
|
+
},
|
|
3700
|
+
plugins: [{
|
|
3701
|
+
name: 'solid:tsrx-dep-scan',
|
|
3702
|
+
async transform(source, id) {
|
|
3703
|
+
if (!isTsrxModule(id) || isTsrxCssModule(id)) return null;
|
|
3704
|
+
const compiler = await loadNativeCompiler();
|
|
3705
|
+
const result = await compiler.transformAsync(source, {
|
|
3706
|
+
...getSolidOptions(options, false, replaceDev, isTestMode),
|
|
3707
|
+
filename: cleanModuleId(id),
|
|
3708
|
+
sourceMap: false
|
|
3709
|
+
});
|
|
3710
|
+
const stripped = await vite.transformWithOxc(result.code, cleanModuleId(id) + '.tsx', {
|
|
3711
|
+
lang: 'tsx',
|
|
3712
|
+
sourcemap: false,
|
|
3713
|
+
target: 'esnext'
|
|
3714
|
+
});
|
|
3715
|
+
return {
|
|
3716
|
+
code: stripped.code,
|
|
3717
|
+
map: null
|
|
3718
|
+
};
|
|
3719
|
+
}
|
|
3720
|
+
}]
|
|
3498
3721
|
}
|
|
3499
3722
|
},
|
|
3500
3723
|
...(Object.keys(test).length ? {
|
|
@@ -3553,8 +3776,13 @@ function solidPlugin(options = {}) {
|
|
|
3553
3776
|
resolve: projectRoot
|
|
3554
3777
|
});
|
|
3555
3778
|
styleFilter = createStyleFilter(projectRoot);
|
|
3556
|
-
|
|
3557
|
-
|
|
3779
|
+
// `components: 'external'` is the acknowledgement that a composing
|
|
3780
|
+
// host (e.g. the Astro adapter or TanStack Start's Solid integration)
|
|
3781
|
+
// owns the document wiring itself — behavior is identical to `true`,
|
|
3782
|
+
// only this warning is skipped. Under SSR start mode it's redundant
|
|
3783
|
+
// but harmless (treated exactly as `true`).
|
|
3784
|
+
if (serverComponents && serverComponentsOption !== 'external' && !(options.start && options.ssr)) {
|
|
3785
|
+
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.");
|
|
3558
3786
|
}
|
|
3559
3787
|
needHmr = config.command === 'serve' && config.mode !== 'production' && options.hot !== false && !options.refresh?.disabled;
|
|
3560
3788
|
},
|
|
@@ -3592,9 +3820,21 @@ function solidPlugin(options = {}) {
|
|
|
3592
3820
|
return origSend(...args);
|
|
3593
3821
|
};
|
|
3594
3822
|
},
|
|
3595
|
-
hotUpdate({
|
|
3596
|
-
|
|
3823
|
+
async hotUpdate({
|
|
3824
|
+
file,
|
|
3825
|
+
modules,
|
|
3826
|
+
read
|
|
3597
3827
|
}) {
|
|
3828
|
+
if (isTsrxModule(file) && this.environment.name === 'client') {
|
|
3829
|
+
updateTsrxCss(tsrxCss, file, await compileTsrxCss(await read(), file));
|
|
3830
|
+
const cssModule = this.environment.moduleGraph.getModuleById(resolvedTsrxCssModuleId(file));
|
|
3831
|
+
if (cssModule) {
|
|
3832
|
+
this.environment.moduleGraph.invalidateModule(cssModule);
|
|
3833
|
+
if (!modules.includes(cssModule)) modules = [...modules, cssModule];
|
|
3834
|
+
return modules;
|
|
3835
|
+
}
|
|
3836
|
+
}
|
|
3837
|
+
|
|
3598
3838
|
// solid-refresh only injects HMR boundaries into client modules, so
|
|
3599
3839
|
// non-client environments have no accept handlers. Without this, Vite
|
|
3600
3840
|
// would see no boundaries and send full-reload messages that race with
|
|
@@ -3613,11 +3853,26 @@ function solidPlugin(options = {}) {
|
|
|
3613
3853
|
this.environment.hot.send({
|
|
3614
3854
|
type: 'full-reload'
|
|
3615
3855
|
});
|
|
3856
|
+
// Server-only modules are the exception to the suppression: a file
|
|
3857
|
+
// with no modules in the client graph has no browser HMR path at
|
|
3858
|
+
// all — nothing client-side accepts it, so staying silent leaves
|
|
3859
|
+
// the browser rendering stale server output until a manual refresh
|
|
3860
|
+
// (e.g. the document shell, which only the server ever imports;
|
|
3861
|
+
// solidjs/solid#3151). Reload the page: the honest cost, and there
|
|
3862
|
+
// is no client update to race with by construction.
|
|
3863
|
+
const clientEnv = devServer?.environments.client;
|
|
3864
|
+
if (clientEnv && !clientEnv.moduleGraph.getModulesByFile(file)?.size) {
|
|
3865
|
+
clientEnv.hot.send({
|
|
3866
|
+
type: 'full-reload'
|
|
3867
|
+
});
|
|
3868
|
+
}
|
|
3616
3869
|
}
|
|
3617
3870
|
return [];
|
|
3618
3871
|
}
|
|
3619
3872
|
},
|
|
3620
3873
|
resolveId(id) {
|
|
3874
|
+
const tsrxCssId = resolveTsrxCssModule(id);
|
|
3875
|
+
if (tsrxCssId) return tsrxCssId;
|
|
3621
3876
|
if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_VIRTUAL_MANIFEST_ID;
|
|
3622
3877
|
},
|
|
3623
3878
|
moduleParsed(info) {
|
|
@@ -3630,7 +3885,7 @@ function solidPlugin(options = {}) {
|
|
|
3630
3885
|
for (const depId of info.dynamicallyImportedIds || []) {
|
|
3631
3886
|
const cleanId = depId.split('?')[0];
|
|
3632
3887
|
if (/node_modules/.test(cleanId) || cleanId.startsWith('\0')) continue;
|
|
3633
|
-
if (
|
|
3888
|
+
if (!(/\.[mc]?[tj]sx?$/i.test(cleanId) || isTsrxModule(cleanId))) continue;
|
|
3634
3889
|
if (emittedLazyChunks.has(depId)) continue;
|
|
3635
3890
|
emittedLazyChunks.add(depId);
|
|
3636
3891
|
emittedLazyChunkRefs.push(this.emitFile({
|
|
@@ -3641,6 +3896,8 @@ function solidPlugin(options = {}) {
|
|
|
3641
3896
|
}
|
|
3642
3897
|
},
|
|
3643
3898
|
load(id) {
|
|
3899
|
+
const tsrxSource = tsrxCssSourceId(id);
|
|
3900
|
+
if (tsrxSource) return tsrxCss.get(tsrxSource) ?? '';
|
|
3644
3901
|
if (id === RESOLVED_VIRTUAL_MANIFEST_ID) {
|
|
3645
3902
|
if (!isBuild) {
|
|
3646
3903
|
return devManifestCode(projectRoot, base, devServer ? devManifestBridgeUrl(devServer) : null);
|
|
@@ -3682,6 +3939,7 @@ function solidPlugin(options = {}) {
|
|
|
3682
3939
|
}
|
|
3683
3940
|
},
|
|
3684
3941
|
async transform(source, id, transformOptions) {
|
|
3942
|
+
if (isTsrxCssModule(id)) return null;
|
|
3685
3943
|
const isSsr = getEnvironmentConsumer(this.environment, transformOptions) === 'server';
|
|
3686
3944
|
const currentFileExtension = getExtension(id);
|
|
3687
3945
|
const extensionsToWatch = options.extensions || [];
|
|
@@ -3697,14 +3955,15 @@ function solidPlugin(options = {}) {
|
|
|
3697
3955
|
// while the transform pipeline below works on the clean file path.
|
|
3698
3956
|
const moduleId = id;
|
|
3699
3957
|
id = id.replace(/\?.*$/, '');
|
|
3700
|
-
|
|
3958
|
+
const isTsrx = isTsrxModule(id);
|
|
3959
|
+
if (!(/\.[mc]?[tj]sx$/i.test(id) || isTsrx || allExtensions.includes(currentFileExtension))) {
|
|
3701
3960
|
return null;
|
|
3702
3961
|
}
|
|
3703
3962
|
const inNodeModules = /node_modules/.test(id);
|
|
3704
3963
|
const solidOptions = getSolidOptions(options, !!isSsr, replaceDev, isTestMode);
|
|
3705
3964
|
|
|
3706
3965
|
// We need to know if the current file extension has a typescript options tied to it
|
|
3707
|
-
const shouldBeProcessedWithTypescript = /\.[mc]?tsx$/i.test(id) || extensionsToWatch.some(extension => {
|
|
3966
|
+
const shouldBeProcessedWithTypescript = /\.[mc]?tsx$/i.test(id) || isTsrx || extensionsToWatch.some(extension => {
|
|
3708
3967
|
if (typeof extension === 'string') {
|
|
3709
3968
|
return extension.includes('tsx');
|
|
3710
3969
|
}
|
|
@@ -3716,14 +3975,19 @@ function solidPlugin(options = {}) {
|
|
|
3716
3975
|
if (shouldBeProcessedWithTypescript) {
|
|
3717
3976
|
plugins.push('typescript');
|
|
3718
3977
|
}
|
|
3719
|
-
|
|
3978
|
+
|
|
3979
|
+
// See the documentModuleId declaration: the document shell declines HMR
|
|
3980
|
+
// (no refresh boundary, explicit self-invalidation) so edits full-reload.
|
|
3981
|
+
const isDocumentShell = documentModuleId !== null && id === documentModuleId;
|
|
3982
|
+
const needRefresh = needHmr && !isSsr && !inNodeModules && !isDocumentShell;
|
|
3983
|
+
const declineHmr = isDocumentShell && needHmr && !isSsr;
|
|
3720
3984
|
const babelUserOptions = await getBabelUserOptions(options, source, id, !!isSsr);
|
|
3721
3985
|
|
|
3722
3986
|
// The native compiler picks its parser dialect from the file
|
|
3723
3987
|
// extension; custom extensions registered through `options.extensions`
|
|
3724
3988
|
// are unknown to it, so borrow a standard one matching the configured
|
|
3725
3989
|
// TypeScript-ness.
|
|
3726
|
-
const nativeFilename = /\.(?:[mc]?[jt]s|[jt]sx)$/i.test(id) ? id : id + (shouldBeProcessedWithTypescript ? '.tsx' : '.jsx');
|
|
3990
|
+
const nativeFilename = isTsrx || /\.(?:[mc]?[jt]s|[jt]sx)$/i.test(id) ? id : id + (shouldBeProcessedWithTypescript ? '.tsx' : '.jsx');
|
|
3727
3991
|
|
|
3728
3992
|
// Shared native prelude for every mode: the lazy() module-URL pass,
|
|
3729
3993
|
// then (dev/client/non-node_modules) the solid-refresh HMR pass, both
|
|
@@ -3733,6 +3997,97 @@ function solidPlugin(options = {}) {
|
|
|
3733
3997
|
const compiler = await loadNativeCompiler();
|
|
3734
3998
|
let code = source;
|
|
3735
3999
|
const maps = [];
|
|
4000
|
+
if (isTsrx) {
|
|
4001
|
+
// Solid lowering preserves authored TypeScript annotations; secondary
|
|
4002
|
+
// passes therefore parse the generated module as TSX even though no
|
|
4003
|
+
// template syntax remains.
|
|
4004
|
+
const generatedFilename = id + '.tsx';
|
|
4005
|
+
const babelBaseOptions = {
|
|
4006
|
+
root: projectRoot,
|
|
4007
|
+
filename: id,
|
|
4008
|
+
sourceFileName: id,
|
|
4009
|
+
ast: false,
|
|
4010
|
+
sourceMaps: true,
|
|
4011
|
+
configFile: false,
|
|
4012
|
+
babelrc: false,
|
|
4013
|
+
parserOpts: {
|
|
4014
|
+
plugins
|
|
4015
|
+
}
|
|
4016
|
+
};
|
|
4017
|
+
let css = '';
|
|
4018
|
+
if (options.compiler !== 'babel') {
|
|
4019
|
+
const result = await compiler.transformAsync(code, {
|
|
4020
|
+
...solidOptions,
|
|
4021
|
+
filename: id,
|
|
4022
|
+
sourceMap: true
|
|
4023
|
+
});
|
|
4024
|
+
code = result.code || '';
|
|
4025
|
+
css = nativeTsrxCss(result);
|
|
4026
|
+
maps.push(result.map);
|
|
4027
|
+
if (options.babel) {
|
|
4028
|
+
// The support pass cannot parse authored TSRX. On this route it
|
|
4029
|
+
// intentionally sees the lowered ordinary JavaScript instead.
|
|
4030
|
+
const supportOptions = mergeAnything.mergeAndConcat(babelUserOptions, babelBaseOptions);
|
|
4031
|
+
// This pass sees native-lowered ordinary JavaScript, so do not
|
|
4032
|
+
// route it back through Babel's TSRX parser.
|
|
4033
|
+
supportOptions.filename = generatedFilename;
|
|
4034
|
+
const supportResult = await babel__namespace.transformAsync(code, supportOptions);
|
|
4035
|
+
if (!supportResult) return undefined;
|
|
4036
|
+
code = supportResult.code || '';
|
|
4037
|
+
maps.push(supportResult.map);
|
|
4038
|
+
}
|
|
4039
|
+
} else {
|
|
4040
|
+
const babelOptions = mergeAnything.mergeAndConcat(babelUserOptions, {
|
|
4041
|
+
...babelBaseOptions,
|
|
4042
|
+
plugins: [[solid, solidOptions]]
|
|
4043
|
+
});
|
|
4044
|
+
const result = await babel__namespace.transformAsync(code, babelOptions);
|
|
4045
|
+
if (!result) return undefined;
|
|
4046
|
+
code = result.code || '';
|
|
4047
|
+
css = babelTsrxCss(result);
|
|
4048
|
+
maps.push(result.map);
|
|
4049
|
+
}
|
|
4050
|
+
const lazyResult = await compiler.transformLazyAsync(code, {
|
|
4051
|
+
filename: generatedFilename,
|
|
4052
|
+
sourceMap: true
|
|
4053
|
+
});
|
|
4054
|
+
code = lazyResult.code;
|
|
4055
|
+
maps.push(lazyResult.map);
|
|
4056
|
+
if (needRefresh) {
|
|
4057
|
+
const refreshResult = await compiler.transformRefreshAsync(code, {
|
|
4058
|
+
filename: generatedFilename,
|
|
4059
|
+
bundler: 'vite',
|
|
4060
|
+
fixRender: true,
|
|
4061
|
+
...(typeof options.refresh?.granular === 'boolean' ? {
|
|
4062
|
+
granular: options.refresh.granular
|
|
4063
|
+
} : {}),
|
|
4064
|
+
jsx: false,
|
|
4065
|
+
importSource: REFRESH_RUNTIME_SOURCE,
|
|
4066
|
+
sourceMap: true
|
|
4067
|
+
});
|
|
4068
|
+
code = refreshResult.code;
|
|
4069
|
+
maps.push(refreshResult.map);
|
|
4070
|
+
}
|
|
4071
|
+
code = injectSsrModuleId(await resolveLazyModuleUrls(this, code, id), moduleId, !!isSsr);
|
|
4072
|
+
let map = options.compiler === 'babel' ? combineSourcemaps(maps) : null;
|
|
4073
|
+
updateTsrxCss(tsrxCss, id, css);
|
|
4074
|
+
if (css) {
|
|
4075
|
+
code = prependTsrxCssImport(code, id);
|
|
4076
|
+
map = offsetSourceMapLine(map);
|
|
4077
|
+
}
|
|
4078
|
+
// Vite selects its TypeScript stripping by file extension. Since the
|
|
4079
|
+
// real module identity remains `.tsrx`, strip the annotations here
|
|
4080
|
+
// after Solid lowering instead of handing typed JavaScript to Rollup.
|
|
4081
|
+
const stripped = await vite.transformWithOxc(code, generatedFilename, {
|
|
4082
|
+
lang: 'tsx',
|
|
4083
|
+
sourcemap: map != null,
|
|
4084
|
+
target: 'esnext'
|
|
4085
|
+
}, map ?? undefined);
|
|
4086
|
+
return {
|
|
4087
|
+
code: stripped.code,
|
|
4088
|
+
map: map == null ? null : stripped.map
|
|
4089
|
+
};
|
|
4090
|
+
}
|
|
3736
4091
|
const lazyResult = await compiler.transformLazyAsync(code, {
|
|
3737
4092
|
filename: nativeFilename,
|
|
3738
4093
|
sourceMap: true
|
|
@@ -3788,7 +4143,7 @@ function solidPlugin(options = {}) {
|
|
|
3788
4143
|
maps.push(result.map);
|
|
3789
4144
|
const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
|
|
3790
4145
|
return {
|
|
3791
|
-
code: finalCode,
|
|
4146
|
+
code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
|
|
3792
4147
|
map: combineSourcemaps(maps)
|
|
3793
4148
|
};
|
|
3794
4149
|
}
|
|
@@ -3809,26 +4164,30 @@ function solidPlugin(options = {}) {
|
|
|
3809
4164
|
maps.push(result.map);
|
|
3810
4165
|
const finalCode = injectSsrModuleId(await resolveLazyModuleUrls(this, result.code || '', id), moduleId, !!isSsr);
|
|
3811
4166
|
return {
|
|
3812
|
-
code: finalCode,
|
|
4167
|
+
code: declineHmr ? finalCode + DOCUMENT_HMR_DECLINE : finalCode,
|
|
3813
4168
|
map: combineSourcemaps(maps)
|
|
3814
4169
|
};
|
|
3815
4170
|
}
|
|
3816
4171
|
};
|
|
3817
4172
|
|
|
3818
|
-
//
|
|
3819
|
-
//
|
|
3820
|
-
//
|
|
3821
|
-
//
|
|
3822
|
-
const
|
|
4173
|
+
// Ordinary modules need the directive transform before JSX. Authored TSRX
|
|
4174
|
+
// cannot be parsed by that standalone pass, so its companion compiler runs
|
|
4175
|
+
// after mainPlugin has lowered the file to ordinary JavaScript while keeping
|
|
4176
|
+
// the original .tsrx id for stable server-function hashes.
|
|
4177
|
+
const serverFunctionPlugins = options.serverFunctions ? serverFunctions(options.serverFunctions === true ? {} : options.serverFunctions, {
|
|
3823
4178
|
devMiddleware: true,
|
|
3824
4179
|
externalDevServer,
|
|
4180
|
+
tsrxAfterSolid: true,
|
|
4181
|
+
tsrxSourceMap: options.compiler === 'babel',
|
|
3825
4182
|
// With start mode on (either variant), the dev middleware dispatches
|
|
3826
4183
|
// the endpoint through the SSR handler so user middleware and the
|
|
3827
4184
|
// stub-backed request event front it exactly like page SSR.
|
|
3828
4185
|
...(startOptions ? {
|
|
3829
4186
|
ssrHandler: SSR_HANDLER_ID
|
|
3830
4187
|
} : {})
|
|
3831
|
-
})
|
|
4188
|
+
}) : [];
|
|
4189
|
+
const tsrxServerFunctionPlugin = serverFunctionPlugins.find(plugin => plugin.name === 'solid:server-functions/tsrx-compiler');
|
|
4190
|
+
const plugins = [boundaryModules(), ...serverFunctionPlugins.filter(plugin => plugin !== tsrxServerFunctionPlugin), mainPlugin, ...(tsrxServerFunctionPlugin ? [tsrxServerFunctionPlugin] : [])];
|
|
3832
4191
|
|
|
3833
4192
|
// The `start` option opts into start-mode serving on top of the transforms;
|
|
3834
4193
|
// the `ssr` boolean picks the mode (a bare `ssr: true` keeps the
|
|
@@ -3843,14 +4202,20 @@ function solidPlugin(options = {}) {
|
|
|
3843
4202
|
serverComponents,
|
|
3844
4203
|
ssr: !!options.ssr,
|
|
3845
4204
|
styleFilter: filterDevStyles,
|
|
3846
|
-
diagnostics:
|
|
4205
|
+
diagnostics: options.diagnostics ?? 'auto',
|
|
4206
|
+
onDocumentResolved(documentPath) {
|
|
4207
|
+
// Normalize to forward slashes to match Vite's transform ids.
|
|
4208
|
+
documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
|
|
4209
|
+
}
|
|
3847
4210
|
}));
|
|
3848
4211
|
}
|
|
3849
4212
|
|
|
3850
4213
|
// Agent diagnostics endpoint + injected bridge (dev serve only — the
|
|
3851
|
-
// plugin no-ops itself for builds and preview via `apply
|
|
3852
|
-
|
|
3853
|
-
|
|
4214
|
+
// plugin no-ops itself for builds and preview via `apply`, and in the
|
|
4215
|
+
// default auto mode additionally disables itself unless the app has
|
|
4216
|
+
// `@solidjs/diagnostics` installed).
|
|
4217
|
+
if (options.diagnostics !== false) {
|
|
4218
|
+
plugins.push(solidDiagnostics(options.diagnostics === true ? true : 'auto'));
|
|
3854
4219
|
}
|
|
3855
4220
|
|
|
3856
4221
|
// Builder-mode (environments API) client-before-server build ordering.
|