@solidjs/vite-plugin 3.0.0-next.37 → 3.0.0-next.39
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 +440 -87
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/index.mjs +442 -89
- 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 +17 -9
- package/dist/types/src/server-functions/compile.d.ts +2 -0
- package/dist/types/src/server-functions/index.d.ts +28 -10
- package/dist/types/src/ssr/index.d.ts +6 -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
|
|
|
@@ -146,6 +146,75 @@ function joinBase(base, pathname) {
|
|
|
146
146
|
return (base.endsWith('/') ? base.slice(0, -1) : base) + pathname;
|
|
147
147
|
}
|
|
148
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
|
+
|
|
149
218
|
/**
|
|
150
219
|
* Dev-mode asset resolution: the `virtual:solid-manifest` module exports a
|
|
151
220
|
* resolver function in dev (instead of the static object a build produces),
|
|
@@ -255,6 +324,9 @@ const cssFileRegExp = /\.(css|less|sass|scss|styl|stylus|pcss|postcss|sss)$/;
|
|
|
255
324
|
// importer controls them — so they must not be SSR'd as style tags.
|
|
256
325
|
const nonAmbientQueryRegExp = /[?&](url|inline|raw)\b/;
|
|
257
326
|
const NULL_BYTE_PLACEHOLDER = '/@id/__x00__';
|
|
327
|
+
function isCssModuleUrl(url) {
|
|
328
|
+
return cssFileRegExp.test(url.split('?')[0]) || isTsrxCssModule(url);
|
|
329
|
+
}
|
|
258
330
|
|
|
259
331
|
// Per Vite's convention virtual module ids are prefixed with `\0`, which
|
|
260
332
|
// cannot appear in an HTML attribute (the parser replaces it). Serialize the
|
|
@@ -311,7 +383,7 @@ async function collectModuleDeps(env, file, deps, crawled, filter, onFile, impor
|
|
|
311
383
|
const node = await getModuleNode(env, file, importer);
|
|
312
384
|
if (!node?.id || deps.has(node)) return;
|
|
313
385
|
deps.add(node);
|
|
314
|
-
const isCss =
|
|
386
|
+
const isCss = isCssModuleUrl(node.url);
|
|
315
387
|
if (!isCss && node.file && !node.id.startsWith('\0') && !filter(node.file)) return;
|
|
316
388
|
if (node.file) onFile?.(node.file);
|
|
317
389
|
if (isCss) return;
|
|
@@ -343,8 +415,7 @@ async function collectDevStyleSources(env, files, onFile, filter = defaultStyleF
|
|
|
343
415
|
const seen = new Set();
|
|
344
416
|
for (const node of deps) {
|
|
345
417
|
if (!node.id) continue;
|
|
346
|
-
|
|
347
|
-
if (!cssFileRegExp.test(cleanUrl) || nonAmbientQueryRegExp.test(node.url)) continue;
|
|
418
|
+
if (!isCssModuleUrl(node.url) || nonAmbientQueryRegExp.test(node.url)) continue;
|
|
348
419
|
const id = wrapId(node.id);
|
|
349
420
|
if (seen.has(id)) continue;
|
|
350
421
|
seen.add(id);
|
|
@@ -558,7 +629,11 @@ function boundaryModules() {
|
|
|
558
629
|
}
|
|
559
630
|
|
|
560
631
|
/**
|
|
561
|
-
* 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).
|
|
562
637
|
*
|
|
563
638
|
* Three pieces:
|
|
564
639
|
* - an injected client module (virtual, imported by index.html or the
|
|
@@ -586,6 +661,36 @@ const METHODS = ['begin', 'end', 'active', 'whyDidRun', 'costs'];
|
|
|
586
661
|
|
|
587
662
|
/** How long the endpoint waits for a page to answer before failing the call. */
|
|
588
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
|
+
}
|
|
589
694
|
function diagnosticsClientModuleCode() {
|
|
590
695
|
// Runtime imports resolve to the APP's diagnostics package (see the
|
|
591
696
|
// resolveId assist below) — the page speaks its own package's protocol.
|
|
@@ -612,18 +717,26 @@ function readJsonBody(req) {
|
|
|
612
717
|
req.on('error', reject);
|
|
613
718
|
});
|
|
614
719
|
}
|
|
615
|
-
function solidDiagnostics() {
|
|
720
|
+
function solidDiagnostics(mode = 'auto') {
|
|
616
721
|
let root = process.cwd();
|
|
617
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;
|
|
618
727
|
return {
|
|
619
728
|
name: 'solid:diagnostics',
|
|
620
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.
|
|
621
733
|
apply(_config, env) {
|
|
622
|
-
return env.command === 'serve' && !env.isPreview;
|
|
734
|
+
return env.command === 'serve' && !env.isPreview && env.mode !== 'test';
|
|
623
735
|
},
|
|
624
736
|
configResolved(config) {
|
|
625
737
|
root = config.root;
|
|
626
738
|
base = config.base;
|
|
739
|
+
if (mode === 'auto') enabled = detectDiagnosticsPackage(root);
|
|
627
740
|
},
|
|
628
741
|
async resolveId(source, importer) {
|
|
629
742
|
if (source === DIAGNOSTICS_CLIENT_ID) {
|
|
@@ -639,7 +752,7 @@ function solidDiagnostics() {
|
|
|
639
752
|
skipSelf: true
|
|
640
753
|
});
|
|
641
754
|
if (!resolved || resolved.id.startsWith('__vite-optional-peer-dep:')) {
|
|
642
|
-
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.');
|
|
643
756
|
}
|
|
644
757
|
return resolved;
|
|
645
758
|
}
|
|
@@ -652,6 +765,7 @@ function solidDiagnostics() {
|
|
|
652
765
|
// Plain (index.html) apps get the client module injected here;
|
|
653
766
|
// start-mode apps import it from the generated client entry instead.
|
|
654
767
|
transformIndexHtml() {
|
|
768
|
+
if (!enabled) return undefined;
|
|
655
769
|
return [{
|
|
656
770
|
tag: 'script',
|
|
657
771
|
attrs: {
|
|
@@ -662,6 +776,11 @@ function solidDiagnostics() {
|
|
|
662
776
|
}];
|
|
663
777
|
},
|
|
664
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
|
+
|
|
665
784
|
// Announce the surface in the startup block. This is a discovery
|
|
666
785
|
// channel: agents watching dev-server output learn the endpoint and
|
|
667
786
|
// the skill documents without any project-level pointer (AGENTS.md).
|
|
@@ -670,7 +789,7 @@ function solidDiagnostics() {
|
|
|
670
789
|
originalPrintUrls();
|
|
671
790
|
const local = server.resolvedUrls?.local[0];
|
|
672
791
|
const endpoint = local ? new URL(DIAGNOSTICS_ENDPOINT, local).href : DIAGNOSTICS_ENDPOINT;
|
|
673
|
-
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`);
|
|
674
793
|
};
|
|
675
794
|
const pending = new Map();
|
|
676
795
|
let nextId = 1;
|
|
@@ -681,6 +800,11 @@ function solidDiagnostics() {
|
|
|
681
800
|
clearTimeout(entry.timer);
|
|
682
801
|
entry.resolve(data);
|
|
683
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.
|
|
684
808
|
server.middlewares.use(DIAGNOSTICS_ENDPOINT, async (req, res) => {
|
|
685
809
|
// The middleware mounts on the exact path; anything deeper is 404.
|
|
686
810
|
if (req.url && req.url !== '/' && req.url !== '') {
|
|
@@ -803,7 +927,7 @@ async function compile(id, code, options) {
|
|
|
803
927
|
mode: options.mode,
|
|
804
928
|
env: options.env,
|
|
805
929
|
directive: options.directive,
|
|
806
|
-
sourceMap:
|
|
930
|
+
sourceMap: options.sourceMap !== false,
|
|
807
931
|
register: options.definitions.register,
|
|
808
932
|
create: options.definitions.create
|
|
809
933
|
});
|
|
@@ -929,11 +1053,11 @@ function xxHash32(buffer, seed = 0) {
|
|
|
929
1053
|
* root — not the invocation directory — so running `vite` from outside the
|
|
930
1054
|
* project keeps compiling the same files. Absolute patterns are used as-is.
|
|
931
1055
|
*
|
|
932
|
-
* @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}"
|
|
933
1057
|
*/
|
|
934
1058
|
|
|
935
|
-
const DEFAULT_INCLUDE = 'src/**/*.{jsx,tsx,ts,js,mjs,cjs}';
|
|
936
|
-
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}';
|
|
937
1061
|
const DEFAULT_MANIFEST = 'virtual:solid-server-function-manifest';
|
|
938
1062
|
const DEFAULT_DIRECTIVE = 'use server';
|
|
939
1063
|
const DEFAULT_RUNTIME = '@solidjs/web/server-functions';
|
|
@@ -954,29 +1078,50 @@ const HANDLER_ID$1 = 'virtual:solid-server-function-handler';
|
|
|
954
1078
|
// (`vite build` then `vite build --ssr`) does not, so the client build
|
|
955
1079
|
// persists its findings for the SSR build to merge (mirroring the plugin's
|
|
956
1080
|
// dist/client/.vite/manifest.json convention).
|
|
1081
|
+
//
|
|
1082
|
+
// The file doubles as the build's statement of which server functions the
|
|
1083
|
+
// CLIENT can reach — every reference the client compile emitted, by wire id
|
|
1084
|
+
// — for build tooling that needs that set without re-deriving it from
|
|
1085
|
+
// compiled output (a static-site prerenderer checking that each reachable
|
|
1086
|
+
// function was captured, for example). Paths are root-relative, posix.
|
|
957
1087
|
const PERSISTED_MANIFEST_PATH = '.vite/solid-server-functions.json';
|
|
1088
|
+
|
|
1089
|
+
/** The persisted manifest's on-disk shape (the array form is the pre-`functions` legacy). */
|
|
1090
|
+
|
|
958
1091
|
function readPersistedManifest(root) {
|
|
959
1092
|
const file = path.resolve(root, 'dist/client', PERSISTED_MANIFEST_PATH);
|
|
960
1093
|
if (!existsSync(file)) return new Set();
|
|
961
1094
|
try {
|
|
962
|
-
const
|
|
1095
|
+
const parsed = JSON.parse(readFileSync(file, 'utf-8'));
|
|
1096
|
+
const entries = Array.isArray(parsed) ? parsed : parsed.modules;
|
|
963
1097
|
return new Set(entries.map(entry => path.resolve(root, entry)).filter(entry => existsSync(entry)));
|
|
964
1098
|
} catch {
|
|
965
1099
|
return new Set();
|
|
966
1100
|
}
|
|
967
1101
|
}
|
|
968
|
-
function writePersistedManifest(root, outDir, entries) {
|
|
1102
|
+
function writePersistedManifest(root, outDir, entries, functions) {
|
|
969
1103
|
const file = path.resolve(root, outDir, PERSISTED_MANIFEST_PATH);
|
|
970
1104
|
mkdirSync(path.dirname(file), {
|
|
971
1105
|
recursive: true
|
|
972
1106
|
});
|
|
973
|
-
const relative =
|
|
974
|
-
|
|
1107
|
+
const relative = entry => path.relative(root, entry).split(path.sep).join('/');
|
|
1108
|
+
const manifest = {
|
|
1109
|
+
modules: [...entries].map(relative),
|
|
1110
|
+
functions: [...functions].map(([id, record]) => ({
|
|
1111
|
+
id,
|
|
1112
|
+
name: record.name,
|
|
1113
|
+
module: relative(record.module)
|
|
1114
|
+
}))
|
|
1115
|
+
};
|
|
1116
|
+
writeFileSync(file, JSON.stringify(manifest, null, 2));
|
|
975
1117
|
}
|
|
976
1118
|
function createManifest() {
|
|
977
1119
|
return {
|
|
978
|
-
|
|
979
|
-
|
|
1120
|
+
modules: {
|
|
1121
|
+
server: new Set(),
|
|
1122
|
+
client: new Set()
|
|
1123
|
+
},
|
|
1124
|
+
clientFunctions: new Map()
|
|
980
1125
|
};
|
|
981
1126
|
}
|
|
982
1127
|
function createDeferredPromise() {
|
|
@@ -1184,13 +1329,13 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1184
1329
|
const hashIndex = new Map();
|
|
1185
1330
|
let hashIndexSize = -1;
|
|
1186
1331
|
function moduleForFunctionId(functionId) {
|
|
1187
|
-
if (manifest.server.size !== hashIndexSize) {
|
|
1332
|
+
if (manifest.modules.server.size !== hashIndexSize) {
|
|
1188
1333
|
hashIndex.clear();
|
|
1189
|
-
for (const entry of manifest.server) {
|
|
1334
|
+
for (const entry of manifest.modules.server) {
|
|
1190
1335
|
const relative = path.relative(root, entry).split(path.sep).join('/');
|
|
1191
1336
|
hashIndex.set(xxHash32(relative).toString(16), entry);
|
|
1192
1337
|
}
|
|
1193
|
-
hashIndexSize = manifest.server.size;
|
|
1338
|
+
hashIndexSize = manifest.modules.server.size;
|
|
1194
1339
|
}
|
|
1195
1340
|
return hashIndex.get(functionId.split('-')[1]);
|
|
1196
1341
|
}
|
|
@@ -1298,6 +1443,62 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1298
1443
|
}
|
|
1299
1444
|
});
|
|
1300
1445
|
}
|
|
1446
|
+
async function transformModule(ctx, code, fileId, opts, tsrx) {
|
|
1447
|
+
const mode = getEnvironmentConsumer(ctx.environment, opts);
|
|
1448
|
+
const [id] = fileId.split('?');
|
|
1449
|
+
if (!id || !filter(id) || isTsrxModule(id) !== tsrx) return null;
|
|
1450
|
+
|
|
1451
|
+
// The directive has to appear literally, so anything without the
|
|
1452
|
+
// substring can skip the native parse entirely.
|
|
1453
|
+
if (!code.includes(directive)) return null;
|
|
1454
|
+
const result = await compile(id, code, {
|
|
1455
|
+
...(mode === 'server' ? serverOptions : clientOptions),
|
|
1456
|
+
mode,
|
|
1457
|
+
env,
|
|
1458
|
+
root,
|
|
1459
|
+
sourceMap: !tsrx || !!internal.tsrxSourceMap
|
|
1460
|
+
});
|
|
1461
|
+
if (!result.valid) return null;
|
|
1462
|
+
|
|
1463
|
+
// The client compile is the authority on what the browser can dispatch:
|
|
1464
|
+
// record every reference it emitted, by wire id, for the persisted
|
|
1465
|
+
// manifest. A module is re-transformed on change, so its previous ids
|
|
1466
|
+
// are dropped first (a renamed function must not linger as reachable).
|
|
1467
|
+
if (mode === 'client') {
|
|
1468
|
+
for (const [functionId, record] of manifest.clientFunctions) {
|
|
1469
|
+
if (record.module === id) manifest.clientFunctions.delete(functionId);
|
|
1470
|
+
}
|
|
1471
|
+
for (const fn of result.functions) {
|
|
1472
|
+
manifest.clientFunctions.set(fn.id, {
|
|
1473
|
+
name: fn.name,
|
|
1474
|
+
module: id
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
}
|
|
1478
|
+
const preloader = preload[mode];
|
|
1479
|
+
if (preloader) preloader.defer();
|
|
1480
|
+
invalidateModules(currentServer, mergeManifestRecord(manifest.modules.server, new Set([id])), manifestId);
|
|
1481
|
+
return {
|
|
1482
|
+
// Appended (not prepended) so the source map for the compiled module
|
|
1483
|
+
// stays valid; imports hoist and the endpoint is only read at call time.
|
|
1484
|
+
code: (result.code || '') + endpointConfigureSnippet(mode),
|
|
1485
|
+
map: result.map
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
const compilerPlugin = {
|
|
1489
|
+
name: 'solid:server-functions/compiler',
|
|
1490
|
+
enforce: 'pre',
|
|
1491
|
+
transform(code, fileId, opts) {
|
|
1492
|
+
return transformModule(this, code, fileId, opts, false);
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
const tsrxCompilerPlugin = {
|
|
1496
|
+
name: 'solid:server-functions/tsrx-compiler',
|
|
1497
|
+
enforce: 'pre',
|
|
1498
|
+
transform(code, fileId, opts) {
|
|
1499
|
+
return transformModule(this, code, fileId, opts, true);
|
|
1500
|
+
}
|
|
1501
|
+
};
|
|
1301
1502
|
return [{
|
|
1302
1503
|
name: 'solid:server-functions/setup',
|
|
1303
1504
|
enforce: 'pre',
|
|
@@ -1324,7 +1525,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1324
1525
|
// build discovered so the server manifest registers them even when
|
|
1325
1526
|
// the SSR module graph never imports them.
|
|
1326
1527
|
for (const entry of readPersistedManifest(root)) {
|
|
1327
|
-
manifest.server.add(entry);
|
|
1528
|
+
manifest.modules.server.add(entry);
|
|
1328
1529
|
}
|
|
1329
1530
|
}
|
|
1330
1531
|
},
|
|
@@ -1339,7 +1540,7 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1339
1540
|
const consumer = ctx.environment?.config?.consumer;
|
|
1340
1541
|
const isClient = consumer ? consumer === 'client' : !isSsrBuild;
|
|
1341
1542
|
if (isBuild && isClient) {
|
|
1342
|
-
writePersistedManifest(root, outDir, manifest.server);
|
|
1543
|
+
writePersistedManifest(root, outDir, manifest.modules.server, manifest.clientFunctions);
|
|
1343
1544
|
}
|
|
1344
1545
|
}
|
|
1345
1546
|
}, {
|
|
@@ -1364,54 +1565,17 @@ function serverFunctions(options = {}, internal = {}) {
|
|
|
1364
1565
|
// configs resolve before the client build has written the file,
|
|
1365
1566
|
// but this load runs once the SSR environment builds — after it.
|
|
1366
1567
|
for (const entry of readPersistedManifest(root)) {
|
|
1367
|
-
manifest.server.add(entry);
|
|
1568
|
+
manifest.modules.server.add(entry);
|
|
1368
1569
|
}
|
|
1369
1570
|
}
|
|
1370
|
-
const current = new Debouncer(() => [...manifest[mode]].map(entry => `import ${JSON.stringify(entry)};`).join('\n'));
|
|
1571
|
+
const current = new Debouncer(() => [...manifest.modules[mode]].map(entry => `import ${JSON.stringify(entry)};`).join('\n'));
|
|
1371
1572
|
preload[mode] = current;
|
|
1372
1573
|
const result = await current.promise.reference;
|
|
1373
1574
|
return result;
|
|
1374
1575
|
}
|
|
1375
1576
|
return null;
|
|
1376
1577
|
}
|
|
1377
|
-
},
|
|
1378
|
-
name: 'solid:server-functions/compiler',
|
|
1379
|
-
enforce: 'pre',
|
|
1380
|
-
async transform(code, fileId, opts) {
|
|
1381
|
-
const mode = getEnvironmentConsumer(this.environment, opts);
|
|
1382
|
-
const [id] = fileId.split('?');
|
|
1383
|
-
if (!filter(id)) {
|
|
1384
|
-
return null;
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
// Fast path: the directive has to appear literally, so anything
|
|
1388
|
-
// without the substring can skip the native parse entirely.
|
|
1389
|
-
if (!code.includes(directive)) {
|
|
1390
|
-
return null;
|
|
1391
|
-
}
|
|
1392
|
-
const result = await compile(id, code, {
|
|
1393
|
-
...(mode === 'server' ? serverOptions : clientOptions),
|
|
1394
|
-
mode,
|
|
1395
|
-
env,
|
|
1396
|
-
root
|
|
1397
|
-
});
|
|
1398
|
-
if (result.valid) {
|
|
1399
|
-
const preloader = preload[mode];
|
|
1400
|
-
if (preloader) {
|
|
1401
|
-
preloader.defer();
|
|
1402
|
-
}
|
|
1403
|
-
invalidateModules(currentServer, mergeManifestRecord(manifest.server, new Set([id])), manifestId);
|
|
1404
|
-
return {
|
|
1405
|
-
// Appended (not prepended) so the source map for the compiled
|
|
1406
|
-
// module stays valid; imports hoist and the endpoint is only
|
|
1407
|
-
// read at call time, never during module evaluation.
|
|
1408
|
-
code: (result.code || '') + endpointConfigureSnippet(mode),
|
|
1409
|
-
map: result.map
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
return null;
|
|
1413
|
-
}
|
|
1414
|
-
}, ...startPlugins];
|
|
1578
|
+
}, compilerPlugin, ...(internal.tsrxAfterSolid ? [tsrxCompilerPlugin] : []), ...startPlugins];
|
|
1415
1579
|
}
|
|
1416
1580
|
|
|
1417
1581
|
const DEVTOOLS_PACKAGE = '@solidjs/start-devtools';
|
|
@@ -1506,9 +1670,9 @@ const ERROR_BOUNDARY_ID = 'virtual:solid-ssr-error-boundary.tsx';
|
|
|
1506
1670
|
const MANIFEST_ID = 'virtual:solid-manifest';
|
|
1507
1671
|
const SERVER_FUNCTION_HANDLER_ID = 'virtual:solid-server-function-handler';
|
|
1508
1672
|
const STORAGE_SOURCE = '@solidjs/web/storage';
|
|
1509
|
-
const ENTRY_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.mjs'];
|
|
1510
|
-
const APP_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js'];
|
|
1511
|
-
const DOCUMENT_EXTENSIONS = ['.tsx', '.jsx'];
|
|
1673
|
+
const ENTRY_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.mjs', '.tsrx'];
|
|
1674
|
+
const APP_EXTENSIONS = ['.tsx', '.jsx', '.ts', '.js', '.tsrx'];
|
|
1675
|
+
const DOCUMENT_EXTENSIONS = ['.tsx', '.jsx', '.tsrx'];
|
|
1512
1676
|
function probe(root, stem, extensions) {
|
|
1513
1677
|
for (const ext of extensions) {
|
|
1514
1678
|
if (existsSync(path.resolve(root, stem + ext))) return stem + ext;
|
|
@@ -1616,7 +1780,9 @@ function startServe(options, internal = {}) {
|
|
|
1616
1780
|
const serverComponents = !!internal.serverComponents;
|
|
1617
1781
|
const errorBoundary = options.errorBoundary !== false;
|
|
1618
1782
|
const styleFilter = internal.styleFilter;
|
|
1619
|
-
|
|
1783
|
+
// `'auto'` resolves against the project root in configResolved, before
|
|
1784
|
+
// any of the (lazy) uses in entry codegen and the entry transform.
|
|
1785
|
+
let diagnostics = internal.diagnostics === true;
|
|
1620
1786
|
let devtoolsEnabled = false;
|
|
1621
1787
|
let devtoolsResolutions = {};
|
|
1622
1788
|
let devtoolsIds = {};
|
|
@@ -2126,6 +2292,12 @@ function startServe(options, internal = {}) {
|
|
|
2126
2292
|
root = config.root;
|
|
2127
2293
|
base = config.base;
|
|
2128
2294
|
isBuild = config.command === 'build';
|
|
2295
|
+
// Test mode excluded for the same reason as the surface plugin's
|
|
2296
|
+
// `apply`: vitest runs a dev serve, and test pages should not get
|
|
2297
|
+
// the bridge import injected into their client entries.
|
|
2298
|
+
if (internal.diagnostics === 'auto' && !isBuild && config.mode !== 'test') {
|
|
2299
|
+
diagnostics = detectDiagnosticsPackage(root);
|
|
2300
|
+
}
|
|
2129
2301
|
},
|
|
2130
2302
|
resolveId(source, importer, opts) {
|
|
2131
2303
|
if (source === HANDLER_ID) {
|
|
@@ -3242,7 +3414,8 @@ function solidPlugin(options = {}) {
|
|
|
3242
3414
|
// resolve against the Vite root, not process.cwd() — running `vite` from
|
|
3243
3415
|
// outside the project would otherwise change what the filter matches.
|
|
3244
3416
|
let filter = createFilter(options.include, options.exclude);
|
|
3245
|
-
const
|
|
3417
|
+
const serverComponentsOption = typeof options.serverFunctions === 'object' ? options.serverFunctions.components : undefined;
|
|
3418
|
+
const serverComponents = !!serverComponentsOption;
|
|
3246
3419
|
// `start: true` is sugar for the empty options bag — one start mode,
|
|
3247
3420
|
// two spellings — so normalize here and let everything downstream see a
|
|
3248
3421
|
// single shape (`false` behaves exactly like omission).
|
|
@@ -3292,6 +3465,7 @@ function solidPlugin(options = {}) {
|
|
|
3292
3465
|
let base = '/';
|
|
3293
3466
|
let clientOutDir = null;
|
|
3294
3467
|
let solidPkgsConfig;
|
|
3468
|
+
const tsrxCss = new Map();
|
|
3295
3469
|
|
|
3296
3470
|
// The client build's manifest, read back by SSR builds. In builder-mode
|
|
3297
3471
|
// (single process, e.g. SolidStart's nitro plugin) the client build runs
|
|
@@ -3386,6 +3560,44 @@ function solidPlugin(options = {}) {
|
|
|
3386
3560
|
const relativeId = path.relative(projectRoot, file).split(path.sep).join('/') + query;
|
|
3387
3561
|
return code + `\nexport const $$moduleUrl = ${JSON.stringify(relativeId)};\n`;
|
|
3388
3562
|
}
|
|
3563
|
+
function nativeTsrxCss(result) {
|
|
3564
|
+
const css = result.css;
|
|
3565
|
+
return typeof css === 'string' ? css : '';
|
|
3566
|
+
}
|
|
3567
|
+
function babelTsrxCss(result) {
|
|
3568
|
+
const css = result.metadata?.css;
|
|
3569
|
+
return typeof css === 'string' ? css : '';
|
|
3570
|
+
}
|
|
3571
|
+
async function compileTsrxCss(source, id) {
|
|
3572
|
+
const solidOptions = getSolidOptions(options, false, replaceDev, isTestMode);
|
|
3573
|
+
if (options.compiler === 'babel') {
|
|
3574
|
+
const babelUserOptions = await getBabelUserOptions(options, source, id, false);
|
|
3575
|
+
const babelOptions = mergeAndConcat(babelUserOptions, {
|
|
3576
|
+
root: projectRoot,
|
|
3577
|
+
// Keep .tsrx: the Babel plugin uses it to select its TSRX parser.
|
|
3578
|
+
filename: id,
|
|
3579
|
+
sourceFileName: id,
|
|
3580
|
+
ast: false,
|
|
3581
|
+
code: false,
|
|
3582
|
+
sourceMaps: false,
|
|
3583
|
+
configFile: false,
|
|
3584
|
+
babelrc: false,
|
|
3585
|
+
parserOpts: {
|
|
3586
|
+
plugins: ['jsx', 'decorators', 'typescript']
|
|
3587
|
+
},
|
|
3588
|
+
plugins: [[solid, solidOptions]]
|
|
3589
|
+
});
|
|
3590
|
+
const result = await babel.transformAsync(source, babelOptions);
|
|
3591
|
+
return result ? babelTsrxCss(result) : '';
|
|
3592
|
+
}
|
|
3593
|
+
const compiler = await loadNativeCompiler();
|
|
3594
|
+
const result = await compiler.transformAsync(source, {
|
|
3595
|
+
...solidOptions,
|
|
3596
|
+
filename: id,
|
|
3597
|
+
sourceMap: false
|
|
3598
|
+
});
|
|
3599
|
+
return nativeTsrxCss(result);
|
|
3600
|
+
}
|
|
3389
3601
|
const mainPlugin = {
|
|
3390
3602
|
name: 'solid',
|
|
3391
3603
|
enforce: 'pre',
|
|
@@ -3476,6 +3688,7 @@ function solidPlugin(options = {}) {
|
|
|
3476
3688
|
dedupe: nestedDeps
|
|
3477
3689
|
},
|
|
3478
3690
|
optimizeDeps: {
|
|
3691
|
+
extensions: ['.tsrx'],
|
|
3479
3692
|
include: [...nestedDeps,
|
|
3480
3693
|
// Dev refresh wrappers import the solid-js/refresh runtime in
|
|
3481
3694
|
// every mode; pre-bundle it up front so its discovery doesn't
|
|
@@ -3496,7 +3709,28 @@ function solidPlugin(options = {}) {
|
|
|
3496
3709
|
jsx: {
|
|
3497
3710
|
runtime: 'classic'
|
|
3498
3711
|
}
|
|
3499
|
-
}
|
|
3712
|
+
},
|
|
3713
|
+
plugins: [{
|
|
3714
|
+
name: 'solid:tsrx-dep-scan',
|
|
3715
|
+
async transform(source, id) {
|
|
3716
|
+
if (!isTsrxModule(id) || isTsrxCssModule(id)) return null;
|
|
3717
|
+
const compiler = await loadNativeCompiler();
|
|
3718
|
+
const result = await compiler.transformAsync(source, {
|
|
3719
|
+
...getSolidOptions(options, false, replaceDev, isTestMode),
|
|
3720
|
+
filename: cleanModuleId(id),
|
|
3721
|
+
sourceMap: false
|
|
3722
|
+
});
|
|
3723
|
+
const stripped = await transformWithOxc(result.code, cleanModuleId(id) + '.tsx', {
|
|
3724
|
+
lang: 'tsx',
|
|
3725
|
+
sourcemap: false,
|
|
3726
|
+
target: 'esnext'
|
|
3727
|
+
});
|
|
3728
|
+
return {
|
|
3729
|
+
code: stripped.code,
|
|
3730
|
+
map: null
|
|
3731
|
+
};
|
|
3732
|
+
}
|
|
3733
|
+
}]
|
|
3500
3734
|
}
|
|
3501
3735
|
},
|
|
3502
3736
|
...(Object.keys(test).length ? {
|
|
@@ -3555,8 +3789,13 @@ function solidPlugin(options = {}) {
|
|
|
3555
3789
|
resolve: projectRoot
|
|
3556
3790
|
});
|
|
3557
3791
|
styleFilter = createStyleFilter(projectRoot);
|
|
3558
|
-
|
|
3559
|
-
|
|
3792
|
+
// `components: 'external'` is the acknowledgement that a composing
|
|
3793
|
+
// host (e.g. the Astro adapter or TanStack Start's Solid integration)
|
|
3794
|
+
// owns the document wiring itself — behavior is identical to `true`,
|
|
3795
|
+
// only this warning is skipped. Under SSR start mode it's redundant
|
|
3796
|
+
// but harmless (treated exactly as `true`).
|
|
3797
|
+
if (serverComponents && serverComponentsOption !== 'external' && !(options.start && options.ssr)) {
|
|
3798
|
+
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.");
|
|
3560
3799
|
}
|
|
3561
3800
|
needHmr = config.command === 'serve' && config.mode !== 'production' && options.hot !== false && !options.refresh?.disabled;
|
|
3562
3801
|
},
|
|
@@ -3594,10 +3833,21 @@ function solidPlugin(options = {}) {
|
|
|
3594
3833
|
return origSend(...args);
|
|
3595
3834
|
};
|
|
3596
3835
|
},
|
|
3597
|
-
hotUpdate({
|
|
3836
|
+
async hotUpdate({
|
|
3837
|
+
file,
|
|
3598
3838
|
modules,
|
|
3599
|
-
|
|
3839
|
+
read
|
|
3600
3840
|
}) {
|
|
3841
|
+
if (isTsrxModule(file) && this.environment.name === 'client') {
|
|
3842
|
+
updateTsrxCss(tsrxCss, file, await compileTsrxCss(await read(), file));
|
|
3843
|
+
const cssModule = this.environment.moduleGraph.getModuleById(resolvedTsrxCssModuleId(file));
|
|
3844
|
+
if (cssModule) {
|
|
3845
|
+
this.environment.moduleGraph.invalidateModule(cssModule);
|
|
3846
|
+
if (!modules.includes(cssModule)) modules = [...modules, cssModule];
|
|
3847
|
+
return modules;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
|
|
3601
3851
|
// solid-refresh only injects HMR boundaries into client modules, so
|
|
3602
3852
|
// non-client environments have no accept handlers. Without this, Vite
|
|
3603
3853
|
// would see no boundaries and send full-reload messages that race with
|
|
@@ -3634,6 +3884,8 @@ function solidPlugin(options = {}) {
|
|
|
3634
3884
|
}
|
|
3635
3885
|
},
|
|
3636
3886
|
resolveId(id) {
|
|
3887
|
+
const tsrxCssId = resolveTsrxCssModule(id);
|
|
3888
|
+
if (tsrxCssId) return tsrxCssId;
|
|
3637
3889
|
if (id === VIRTUAL_MANIFEST_ID) return RESOLVED_VIRTUAL_MANIFEST_ID;
|
|
3638
3890
|
},
|
|
3639
3891
|
moduleParsed(info) {
|
|
@@ -3646,7 +3898,7 @@ function solidPlugin(options = {}) {
|
|
|
3646
3898
|
for (const depId of info.dynamicallyImportedIds || []) {
|
|
3647
3899
|
const cleanId = depId.split('?')[0];
|
|
3648
3900
|
if (/node_modules/.test(cleanId) || cleanId.startsWith('\0')) continue;
|
|
3649
|
-
if (
|
|
3901
|
+
if (!(/\.[mc]?[tj]sx?$/i.test(cleanId) || isTsrxModule(cleanId))) continue;
|
|
3650
3902
|
if (emittedLazyChunks.has(depId)) continue;
|
|
3651
3903
|
emittedLazyChunks.add(depId);
|
|
3652
3904
|
emittedLazyChunkRefs.push(this.emitFile({
|
|
@@ -3657,6 +3909,8 @@ function solidPlugin(options = {}) {
|
|
|
3657
3909
|
}
|
|
3658
3910
|
},
|
|
3659
3911
|
load(id) {
|
|
3912
|
+
const tsrxSource = tsrxCssSourceId(id);
|
|
3913
|
+
if (tsrxSource) return tsrxCss.get(tsrxSource) ?? '';
|
|
3660
3914
|
if (id === RESOLVED_VIRTUAL_MANIFEST_ID) {
|
|
3661
3915
|
if (!isBuild) {
|
|
3662
3916
|
return devManifestCode(projectRoot, base, devServer ? devManifestBridgeUrl(devServer) : null);
|
|
@@ -3698,6 +3952,7 @@ function solidPlugin(options = {}) {
|
|
|
3698
3952
|
}
|
|
3699
3953
|
},
|
|
3700
3954
|
async transform(source, id, transformOptions) {
|
|
3955
|
+
if (isTsrxCssModule(id)) return null;
|
|
3701
3956
|
const isSsr = getEnvironmentConsumer(this.environment, transformOptions) === 'server';
|
|
3702
3957
|
const currentFileExtension = getExtension(id);
|
|
3703
3958
|
const extensionsToWatch = options.extensions || [];
|
|
@@ -3713,14 +3968,15 @@ function solidPlugin(options = {}) {
|
|
|
3713
3968
|
// while the transform pipeline below works on the clean file path.
|
|
3714
3969
|
const moduleId = id;
|
|
3715
3970
|
id = id.replace(/\?.*$/, '');
|
|
3716
|
-
|
|
3971
|
+
const isTsrx = isTsrxModule(id);
|
|
3972
|
+
if (!(/\.[mc]?[tj]sx$/i.test(id) || isTsrx || allExtensions.includes(currentFileExtension))) {
|
|
3717
3973
|
return null;
|
|
3718
3974
|
}
|
|
3719
3975
|
const inNodeModules = /node_modules/.test(id);
|
|
3720
3976
|
const solidOptions = getSolidOptions(options, !!isSsr, replaceDev, isTestMode);
|
|
3721
3977
|
|
|
3722
3978
|
// We need to know if the current file extension has a typescript options tied to it
|
|
3723
|
-
const shouldBeProcessedWithTypescript = /\.[mc]?tsx$/i.test(id) || extensionsToWatch.some(extension => {
|
|
3979
|
+
const shouldBeProcessedWithTypescript = /\.[mc]?tsx$/i.test(id) || isTsrx || extensionsToWatch.some(extension => {
|
|
3724
3980
|
if (typeof extension === 'string') {
|
|
3725
3981
|
return extension.includes('tsx');
|
|
3726
3982
|
}
|
|
@@ -3744,7 +4000,7 @@ function solidPlugin(options = {}) {
|
|
|
3744
4000
|
// extension; custom extensions registered through `options.extensions`
|
|
3745
4001
|
// are unknown to it, so borrow a standard one matching the configured
|
|
3746
4002
|
// TypeScript-ness.
|
|
3747
|
-
const nativeFilename = /\.(?:[mc]?[jt]s|[jt]sx)$/i.test(id) ? id : id + (shouldBeProcessedWithTypescript ? '.tsx' : '.jsx');
|
|
4003
|
+
const nativeFilename = isTsrx || /\.(?:[mc]?[jt]s|[jt]sx)$/i.test(id) ? id : id + (shouldBeProcessedWithTypescript ? '.tsx' : '.jsx');
|
|
3748
4004
|
|
|
3749
4005
|
// Shared native prelude for every mode: the lazy() module-URL pass,
|
|
3750
4006
|
// then (dev/client/non-node_modules) the solid-refresh HMR pass, both
|
|
@@ -3754,6 +4010,97 @@ function solidPlugin(options = {}) {
|
|
|
3754
4010
|
const compiler = await loadNativeCompiler();
|
|
3755
4011
|
let code = source;
|
|
3756
4012
|
const maps = [];
|
|
4013
|
+
if (isTsrx) {
|
|
4014
|
+
// Solid lowering preserves authored TypeScript annotations; secondary
|
|
4015
|
+
// passes therefore parse the generated module as TSX even though no
|
|
4016
|
+
// template syntax remains.
|
|
4017
|
+
const generatedFilename = id + '.tsx';
|
|
4018
|
+
const babelBaseOptions = {
|
|
4019
|
+
root: projectRoot,
|
|
4020
|
+
filename: id,
|
|
4021
|
+
sourceFileName: id,
|
|
4022
|
+
ast: false,
|
|
4023
|
+
sourceMaps: true,
|
|
4024
|
+
configFile: false,
|
|
4025
|
+
babelrc: false,
|
|
4026
|
+
parserOpts: {
|
|
4027
|
+
plugins
|
|
4028
|
+
}
|
|
4029
|
+
};
|
|
4030
|
+
let css = '';
|
|
4031
|
+
if (options.compiler !== 'babel') {
|
|
4032
|
+
const result = await compiler.transformAsync(code, {
|
|
4033
|
+
...solidOptions,
|
|
4034
|
+
filename: id,
|
|
4035
|
+
sourceMap: true
|
|
4036
|
+
});
|
|
4037
|
+
code = result.code || '';
|
|
4038
|
+
css = nativeTsrxCss(result);
|
|
4039
|
+
maps.push(result.map);
|
|
4040
|
+
if (options.babel) {
|
|
4041
|
+
// The support pass cannot parse authored TSRX. On this route it
|
|
4042
|
+
// intentionally sees the lowered ordinary JavaScript instead.
|
|
4043
|
+
const supportOptions = mergeAndConcat(babelUserOptions, babelBaseOptions);
|
|
4044
|
+
// This pass sees native-lowered ordinary JavaScript, so do not
|
|
4045
|
+
// route it back through Babel's TSRX parser.
|
|
4046
|
+
supportOptions.filename = generatedFilename;
|
|
4047
|
+
const supportResult = await babel.transformAsync(code, supportOptions);
|
|
4048
|
+
if (!supportResult) return undefined;
|
|
4049
|
+
code = supportResult.code || '';
|
|
4050
|
+
maps.push(supportResult.map);
|
|
4051
|
+
}
|
|
4052
|
+
} else {
|
|
4053
|
+
const babelOptions = mergeAndConcat(babelUserOptions, {
|
|
4054
|
+
...babelBaseOptions,
|
|
4055
|
+
plugins: [[solid, solidOptions]]
|
|
4056
|
+
});
|
|
4057
|
+
const result = await babel.transformAsync(code, babelOptions);
|
|
4058
|
+
if (!result) return undefined;
|
|
4059
|
+
code = result.code || '';
|
|
4060
|
+
css = babelTsrxCss(result);
|
|
4061
|
+
maps.push(result.map);
|
|
4062
|
+
}
|
|
4063
|
+
const lazyResult = await compiler.transformLazyAsync(code, {
|
|
4064
|
+
filename: generatedFilename,
|
|
4065
|
+
sourceMap: true
|
|
4066
|
+
});
|
|
4067
|
+
code = lazyResult.code;
|
|
4068
|
+
maps.push(lazyResult.map);
|
|
4069
|
+
if (needRefresh) {
|
|
4070
|
+
const refreshResult = await compiler.transformRefreshAsync(code, {
|
|
4071
|
+
filename: generatedFilename,
|
|
4072
|
+
bundler: 'vite',
|
|
4073
|
+
fixRender: true,
|
|
4074
|
+
...(typeof options.refresh?.granular === 'boolean' ? {
|
|
4075
|
+
granular: options.refresh.granular
|
|
4076
|
+
} : {}),
|
|
4077
|
+
jsx: false,
|
|
4078
|
+
importSource: REFRESH_RUNTIME_SOURCE,
|
|
4079
|
+
sourceMap: true
|
|
4080
|
+
});
|
|
4081
|
+
code = refreshResult.code;
|
|
4082
|
+
maps.push(refreshResult.map);
|
|
4083
|
+
}
|
|
4084
|
+
code = injectSsrModuleId(await resolveLazyModuleUrls(this, code, id), moduleId, !!isSsr);
|
|
4085
|
+
let map = options.compiler === 'babel' ? combineSourcemaps(maps) : null;
|
|
4086
|
+
updateTsrxCss(tsrxCss, id, css);
|
|
4087
|
+
if (css) {
|
|
4088
|
+
code = prependTsrxCssImport(code, id);
|
|
4089
|
+
map = offsetSourceMapLine(map);
|
|
4090
|
+
}
|
|
4091
|
+
// Vite selects its TypeScript stripping by file extension. Since the
|
|
4092
|
+
// real module identity remains `.tsrx`, strip the annotations here
|
|
4093
|
+
// after Solid lowering instead of handing typed JavaScript to Rollup.
|
|
4094
|
+
const stripped = await transformWithOxc(code, generatedFilename, {
|
|
4095
|
+
lang: 'tsx',
|
|
4096
|
+
sourcemap: map != null,
|
|
4097
|
+
target: 'esnext'
|
|
4098
|
+
}, map ?? undefined);
|
|
4099
|
+
return {
|
|
4100
|
+
code: stripped.code,
|
|
4101
|
+
map: map == null ? null : stripped.map
|
|
4102
|
+
};
|
|
4103
|
+
}
|
|
3757
4104
|
const lazyResult = await compiler.transformLazyAsync(code, {
|
|
3758
4105
|
filename: nativeFilename,
|
|
3759
4106
|
sourceMap: true
|
|
@@ -3836,20 +4183,24 @@ function solidPlugin(options = {}) {
|
|
|
3836
4183
|
}
|
|
3837
4184
|
};
|
|
3838
4185
|
|
|
3839
|
-
//
|
|
3840
|
-
//
|
|
3841
|
-
//
|
|
3842
|
-
//
|
|
3843
|
-
const
|
|
4186
|
+
// Ordinary modules need the directive transform before JSX. Authored TSRX
|
|
4187
|
+
// cannot be parsed by that standalone pass, so its companion compiler runs
|
|
4188
|
+
// after mainPlugin has lowered the file to ordinary JavaScript while keeping
|
|
4189
|
+
// the original .tsrx id for stable server-function hashes.
|
|
4190
|
+
const serverFunctionPlugins = options.serverFunctions ? serverFunctions(options.serverFunctions === true ? {} : options.serverFunctions, {
|
|
3844
4191
|
devMiddleware: true,
|
|
3845
4192
|
externalDevServer,
|
|
4193
|
+
tsrxAfterSolid: true,
|
|
4194
|
+
tsrxSourceMap: options.compiler === 'babel',
|
|
3846
4195
|
// With start mode on (either variant), the dev middleware dispatches
|
|
3847
4196
|
// the endpoint through the SSR handler so user middleware and the
|
|
3848
4197
|
// stub-backed request event front it exactly like page SSR.
|
|
3849
4198
|
...(startOptions ? {
|
|
3850
4199
|
ssrHandler: SSR_HANDLER_ID
|
|
3851
4200
|
} : {})
|
|
3852
|
-
})
|
|
4201
|
+
}) : [];
|
|
4202
|
+
const tsrxServerFunctionPlugin = serverFunctionPlugins.find(plugin => plugin.name === 'solid:server-functions/tsrx-compiler');
|
|
4203
|
+
const plugins = [boundaryModules(), ...serverFunctionPlugins.filter(plugin => plugin !== tsrxServerFunctionPlugin), mainPlugin, ...(tsrxServerFunctionPlugin ? [tsrxServerFunctionPlugin] : [])];
|
|
3853
4204
|
|
|
3854
4205
|
// The `start` option opts into start-mode serving on top of the transforms;
|
|
3855
4206
|
// the `ssr` boolean picks the mode (a bare `ssr: true` keeps the
|
|
@@ -3864,7 +4215,7 @@ function solidPlugin(options = {}) {
|
|
|
3864
4215
|
serverComponents,
|
|
3865
4216
|
ssr: !!options.ssr,
|
|
3866
4217
|
styleFilter: filterDevStyles,
|
|
3867
|
-
diagnostics:
|
|
4218
|
+
diagnostics: options.diagnostics ?? 'auto',
|
|
3868
4219
|
onDocumentResolved(documentPath) {
|
|
3869
4220
|
// Normalize to forward slashes to match Vite's transform ids.
|
|
3870
4221
|
documentModuleId = documentPath ? documentPath.split(path.sep).join('/') : null;
|
|
@@ -3873,9 +4224,11 @@ function solidPlugin(options = {}) {
|
|
|
3873
4224
|
}
|
|
3874
4225
|
|
|
3875
4226
|
// Agent diagnostics endpoint + injected bridge (dev serve only — the
|
|
3876
|
-
// plugin no-ops itself for builds and preview via `apply
|
|
3877
|
-
|
|
3878
|
-
|
|
4227
|
+
// plugin no-ops itself for builds and preview via `apply`, and in the
|
|
4228
|
+
// default auto mode additionally disables itself unless the app has
|
|
4229
|
+
// `@solidjs/diagnostics` installed).
|
|
4230
|
+
if (options.diagnostics !== false) {
|
|
4231
|
+
plugins.push(solidDiagnostics(options.diagnostics === true ? true : 'auto'));
|
|
3879
4232
|
}
|
|
3880
4233
|
|
|
3881
4234
|
// Builder-mode (environments API) client-before-server build ordering.
|