diff --git a/packages/docs/global.d.ts b/packages/docs/global.d.ts
new file mode 100644
index 000000000000..ffeb16c99150
--- /dev/null
+++ b/packages/docs/global.d.ts
@@ -0,0 +1,5 @@
+// handled by raw-source plugin in vite.repl-apps.ts
+declare module '*?raw-source' {
+ const url: string;
+ export default url;
+}
diff --git a/packages/docs/package.json b/packages/docs/package.json
index f76dac79c02c..ef4477b95494 100644
--- a/packages/docs/package.json
+++ b/packages/docs/package.json
@@ -33,6 +33,7 @@
"fflate": "0.8.2",
"gray-matter": "4.0.3",
"leaflet": "1.9.4",
+ "magic-string": "0.30.11",
"openai": "3.3.0",
"postcss": "8.4.39",
"prettier": "3.3.3",
diff --git a/packages/docs/src/repl/bundled.tsx b/packages/docs/src/repl/bundled.tsx
index 96c1a6782688..6ce706ac5f17 100644
--- a/packages/docs/src/repl/bundled.tsx
+++ b/packages/docs/src/repl/bundled.tsx
@@ -2,24 +2,22 @@ import { version as qwikVersion } from '@builder.io/qwik';
import type { PkgUrls } from './types';
import prettierPkgJson from 'prettier/package.json';
-import prettierParserHtml from 'prettier/plugins/html.js?raw';
-import prettierStandaloneJs from 'prettier/standalone.js?raw';
+import prettierParserHtml from '../../node_modules/prettier/plugins/html.js?raw-source';
+import prettierStandaloneJs from '../../node_modules/prettier/standalone.js?raw-source';
+
import terserPkgJson from 'terser/package.json';
-import terserJs from '../../node_modules/terser/dist/bundle.min.js?raw';
-import qBuild from '../../node_modules/@builder.io/qwik/dist/build/index.d.ts?raw';
-import qCoreCjs from '../../node_modules/@builder.io/qwik/dist/core.cjs?raw';
-import qCoreDts from '../../node_modules/@builder.io/qwik/dist/core.d.ts?raw';
-import qCoreMinMjs from '../../node_modules/@builder.io/qwik/dist/core.min.mjs?raw';
-import qCoreMjs from '../../node_modules/@builder.io/qwik/dist/core.mjs?raw';
-import qJsxDts from '../../node_modules/@builder.io/qwik/dist/jsx-runtime.d.ts?raw';
-import qOptimizerCjs from '../../node_modules/@builder.io/qwik/dist/optimizer.cjs?raw';
-import qServerCjs from '../../node_modules/@builder.io/qwik/dist/server.cjs?raw';
-import qServerDts from '../../node_modules/@builder.io/qwik/dist/server.d.ts?raw';
-import qWasmCjs from '../../node_modules/@builder.io/qwik/bindings/qwik.wasm.cjs?raw';
-// we can use the wasm binary directly, it doesn't get processed
-import qWasmBinUrl from '../../node_modules/@builder.io/qwik/bindings/qwik_wasm_bg.wasm?url';
+import terserJs from '../../node_modules/terser/dist/bundle.min.js?raw-source';
-import { isServer } from '@builder.io/qwik/build';
+import qBuild from '../../node_modules/@builder.io/qwik/dist/build/index.d.ts?raw-source';
+import qCoreCjs from '../../node_modules/@builder.io/qwik/dist/core.cjs?raw-source';
+import qCoreDts from '../../node_modules/@builder.io/qwik/dist/core.d.ts?raw-source';
+import qCoreMinMjs from '../../node_modules/@builder.io/qwik/dist/core.min.mjs?raw-source';
+import qCoreMjs from '../../node_modules/@builder.io/qwik/dist/core.mjs?raw-source';
+import qOptimizerCjs from '../../node_modules/@builder.io/qwik/dist/optimizer.cjs?raw-source';
+import qServerCjs from '../../node_modules/@builder.io/qwik/dist/server.cjs?raw-source';
+import qServerDts from '../../node_modules/@builder.io/qwik/dist/server.d.ts?raw-source';
+import qWasmCjs from '../../node_modules/@builder.io/qwik/bindings/qwik.wasm.cjs?raw-source';
+import qWasmBinUrl from '../../node_modules/@builder.io/qwik/bindings/qwik_wasm_bg.wasm?raw-source';
export const QWIK_PKG_NAME = '@builder.io/qwik';
const ROLLUP_VERSION = '2.75.6';
@@ -46,35 +44,24 @@ export const getNpmCdnUrl = (
return `https://cdn.jsdelivr.net/npm/${pkgName}${pkgVersion ? '@' + pkgVersion : ''}${pkgPath}`;
};
-// https://github.com/vitejs/vite/issues/15753
-const blobUrl = (code: string, type: string = 'application/javascript') => {
- if (isServer) {
- return '';
- }
- const blob = new Blob([code], { type });
- return URL.createObjectURL(blob);
-};
-
-const bundled: PkgUrls = {
+export const bundled: PkgUrls = {
[QWIK_PKG_NAME]: {
version: qwikVersion,
- '/dist/build/index.d.ts': blobUrl(qBuild),
- '/dist/core.cjs': blobUrl(qCoreCjs),
- '/dist/core.d.ts': blobUrl(qCoreDts),
- '/dist/core.min.mjs': blobUrl(qCoreMinMjs),
- '/dist/core.mjs': blobUrl(qCoreMjs),
- '/dist/jsx-runtime.d.ts': blobUrl(qJsxDts),
- '/dist/jsx-dev-runtime.d.ts': blobUrl(qJsxDts),
- '/dist/optimizer.cjs': blobUrl(qOptimizerCjs),
- '/dist/server.cjs': blobUrl(qServerCjs),
- '/dist/server.d.ts': blobUrl(qServerDts),
- '/bindings/qwik.wasm.cjs': blobUrl(qWasmCjs),
+ '/dist/build/index.d.ts': qBuild,
+ '/dist/core.cjs': qCoreCjs,
+ '/dist/core.d.ts': qCoreDts,
+ '/dist/core.min.mjs': qCoreMinMjs,
+ '/dist/core.mjs': qCoreMjs,
+ '/dist/optimizer.cjs': qOptimizerCjs,
+ '/dist/server.cjs': qServerCjs,
+ '/dist/server.d.ts': qServerDts,
+ '/bindings/qwik.wasm.cjs': qWasmCjs,
'/bindings/qwik_wasm_bg.wasm': qWasmBinUrl,
},
prettier: {
version: prettierPkgJson.version,
- '/plugins/html.js': blobUrl(prettierParserHtml),
- '/standalone.js': blobUrl(prettierStandaloneJs),
+ '/plugins/html.js': prettierParserHtml,
+ '/standalone.js': prettierStandaloneJs,
},
// v4 of rollup uses wasm etc, need to figure out how to bundle that
rollup: {
@@ -88,13 +75,6 @@ const bundled: PkgUrls = {
},
terser: {
version: terserPkgJson.version,
- '/dist/bundle.min.js': blobUrl(terserJs),
+ '/dist/bundle.min.js': terserJs,
},
};
-
-export const getBundled = () =>
- isServer
- ? {
- [QWIK_PKG_NAME]: { version: qwikVersion },
- }
- : bundled;
diff --git a/packages/docs/src/repl/monaco.tsx b/packages/docs/src/repl/monaco.tsx
index e3f0b98822d6..e6698852a620 100644
--- a/packages/docs/src/repl/monaco.tsx
+++ b/packages/docs/src/repl/monaco.tsx
@@ -4,8 +4,11 @@ import type MonacoTypes from 'monaco-editor';
import type { EditorProps, EditorStore } from './editor';
import type { ReplStore } from './types';
import { getColorPreference } from '../components/theme-toggle/theme-toggle';
-import { getBundled, getNpmCdnUrl } from './bundled';
+import { bundled, getNpmCdnUrl } from './bundled';
import { isServer } from '@builder.io/qwik/build';
+// We cannot use this, it causes the repl to use imports
+// import { QWIK_REPL_DEPS_CACHE } from './worker/repl-constants';
+const QWIK_REPL_DEPS_CACHE = 'QwikReplDeps';
export const initMonacoEditor = async (
containerElm: any,
@@ -207,7 +210,10 @@ export const addQwikLibs = async (version: string) => {
);
}
});
-
+ typescriptDefaults.addExtraLib(
+ `declare module '@builder.io/qwik/jsx-runtime' { export * from '@builder.io/qwik' }`,
+ '/node_modules/@builder.io/qwik/dist/jsx-runtime.d.ts'
+ );
typescriptDefaults.addExtraLib(CLIENT_LIB);
};
@@ -226,19 +232,6 @@ const loadDeps = async (qwikVersion: string) => {
pkgPath: `${prefix}core.d.ts`,
import: '',
},
- // JSX runtime
- {
- pkgName: '@builder.io/qwik',
- pkgVersion: qwikVersion,
- pkgPath: `${prefix}jsx-runtime.d.ts`,
- import: '/jsx-runtime',
- },
- {
- pkgName: '@builder.io/qwik',
- pkgVersion: qwikVersion,
- pkgPath: `${prefix}jsx-runtime.d.ts`,
- import: '/jsx-dev-runtime',
- },
// server API
{
pkgName: '@builder.io/qwik',
@@ -255,7 +248,7 @@ const loadDeps = async (qwikVersion: string) => {
},
];
- const cache = await caches.open('QwikReplResults');
+ const cache = await caches.open(QWIK_REPL_DEPS_CACHE);
await Promise.all(
deps.map(async (dep) => {
@@ -286,7 +279,6 @@ const loadDeps = async (qwikVersion: string) => {
return monacoCtx.deps;
};
-const bundled = getBundled();
const fetchDep = async (cache: Cache, dep: NodeModuleDep) => {
const url = getNpmCdnUrl(bundled, dep.pkgName, dep.pkgVersion, dep.pkgPath);
const req = new Request(url);
diff --git a/packages/docs/src/repl/repl-detail-panel.tsx b/packages/docs/src/repl/repl-detail-panel.tsx
index 561dcbea6233..07a9b94ffef9 100644
--- a/packages/docs/src/repl/repl-detail-panel.tsx
+++ b/packages/docs/src/repl/repl-detail-panel.tsx
@@ -1,4 +1,4 @@
-import { QWIK_PKG_NAME, getBundled } from './bundled';
+import { QWIK_PKG_NAME, bundled } from './bundled';
import { ReplConsole } from './repl-console';
import { ReplOptions } from './repl-options';
import { ReplTabButton } from './repl-tab-button';
@@ -31,7 +31,7 @@ export const ReplDetailPanel = ({ input, store }: ReplDetailPanelProps) => {
) : null}
diff --git a/packages/docs/src/repl/repl-version.ts b/packages/docs/src/repl/repl-version.ts
index 347ec10adea6..eed2a554bc9a 100644
--- a/packages/docs/src/repl/repl-version.ts
+++ b/packages/docs/src/repl/repl-version.ts
@@ -1,7 +1,7 @@
/* eslint-disable no-console */
-import { QWIK_PKG_NAME, getBundled } from './bundled';
+import { QWIK_PKG_NAME, bundled } from './bundled';
-const bundledVersion = getBundled()[QWIK_PKG_NAME].version;
+const bundledVersion = bundled[QWIK_PKG_NAME].version;
// The golden oldies
const keepList = new Set('1.0.0,1.1.5,1.2.13,1.4.5'.split(','));
diff --git a/packages/docs/src/repl/repl.tsx b/packages/docs/src/repl/repl.tsx
index 83b770ab1f3a..d54792b1a804 100644
--- a/packages/docs/src/repl/repl.tsx
+++ b/packages/docs/src/repl/repl.tsx
@@ -15,7 +15,7 @@ import type { ReplStore, ReplUpdateMessage, ReplMessage, ReplAppInput } from './
import { ReplDetailPanel } from './repl-detail-panel';
import { getReplVersion } from './repl-version';
import { updateReplOutput } from './repl-output-update';
-import { QWIK_PKG_NAME, getBundled, getNpmCdnUrl } from './bundled';
+import { QWIK_PKG_NAME, bundled, getNpmCdnUrl } from './bundled';
import { isServer } from '@builder.io/qwik/build';
export const Repl = component$((props: ReplProps) => {
@@ -165,7 +165,6 @@ export const receiveMessageFromReplServer = (
}
};
-const bundled = getBundled();
const getDependencies = (input: ReplAppInput) => {
const out = { ...bundled };
if (input.version !== 'bundled') {
diff --git a/packages/docs/vite.config.mts b/packages/docs/vite.config.mts
index 2833e1206a9f..db8be7e51096 100644
--- a/packages/docs/vite.config.mts
+++ b/packages/docs/vite.config.mts
@@ -6,7 +6,7 @@ import { qwikVite } from '@builder.io/qwik/optimizer';
import path, { resolve } from 'node:path';
import { defineConfig, loadEnv } from 'vite';
import Inspect from 'vite-plugin-inspect';
-import { examplesData, playgroundData, tutorialData } from './vite.repl-apps';
+import { examplesData, playgroundData, rawSource, tutorialData } from './vite.repl-apps';
import { sourceResolver } from './vite.source-resolver';
export const PUBLIC_QWIK_INSIGHT_KEY = loadEnv('', '.', 'PUBLIC').PUBLIC_QWIK_INSIGHTS_KEY;
@@ -58,6 +58,7 @@ export default defineConfig(async () => {
},
plugins: [
+ rawSource(),
qwikCity({
mdxPlugins: {
rehypeSyntaxHighlight: false,
@@ -138,7 +139,7 @@ export default defineConfig(async () => {
defaultHandler(level, log);
},
output: {
- assetFileNames: 'assets/[hash].[ext]',
+ assetFileNames: 'assets/[hash]-[name].[ext]',
},
},
},
diff --git a/packages/docs/vite.repl-apps.ts b/packages/docs/vite.repl-apps.ts
index 441796c923e8..7580acd49cdd 100644
--- a/packages/docs/vite.repl-apps.ts
+++ b/packages/docs/vite.repl-apps.ts
@@ -7,6 +7,7 @@ import type { PlaygroundApp } from './src/routes/playground/playground-data';
import type { TutorialSection } from './src/routes/tutorial/tutorial-data';
import type { PluginContext } from 'rollup';
import type { ReplModuleInput } from './src/repl/types';
+import MagicString from 'magic-string';
export function playgroundData(routesDir: string): Plugin {
const playgroundAppDir = join(routesDir, 'playground', 'app');
@@ -258,3 +259,108 @@ export function tutorialData(routesDir: string): Plugin {
},
};
}
+
+// Workaround for https://github.com/vitejs/vite/issues/15753
+// A vite plugin that implements what `?raw&url` should do
+// Use `import url from 'file?raw-source'` to get the url for the raw file content
+export function rawSource(): Plugin {
+ let base: string;
+ let isDev: boolean = false;
+ let doSourceMap: boolean = false;
+ const extToMime = {
+ js: 'application/javascript',
+ css: 'text/css',
+ html: 'text/html',
+ json: 'application/json',
+ wasm: 'application/wasm',
+ };
+ return {
+ name: 'raw-source',
+
+ configResolved(config) {
+ base = config.base;
+ isDev = config.command === 'serve';
+ doSourceMap = !!config.build.sourcemap;
+ },
+
+ configureServer(server) {
+ // Vite still processes /@fs urls, so we need to run our own static server
+ server.middlewares.use((req, res, next) => {
+ if (req.url!.startsWith('/@raw-fs')) {
+ const filePath = req.url!.slice('/@raw-fs'.length);
+ if (existsSync(filePath)) {
+ const ext = filePath.split('.').pop()! as keyof typeof extToMime;
+ const contentType = extToMime[ext] || 'application/octet-stream';
+ res.setHeader('Content-Type', contentType);
+ res.end(readFileSync(filePath));
+ } else {
+ res.statusCode = 404;
+ res.end('File not found');
+ }
+ } else {
+ next();
+ }
+ });
+ },
+
+ resolveId: {
+ order: 'pre',
+ async handler(id, importer) {
+ const match = /^(?.*)\?(|(?.+)&)raw-source($|&(?.*))/.exec(id);
+
+ if (match) {
+ const newQuery = [match.groups!.before, match.groups!.after].filter(Boolean).join('&');
+ const newId = `${match.groups!.path}${newQuery ? `?${newQuery}` : ''}`;
+ const resolved = await this.resolve(newId, importer, {
+ skipSelf: true,
+ });
+ if (!resolved) {
+ throw new Error(`Could not resolve "${id}" from "${importer}"`);
+ }
+ return `\0raw-source:${resolved!.id.split('?')[0]}`;
+ }
+ },
+ },
+
+ load(id) {
+ if (id.startsWith('\0raw-source:')) {
+ let path = id.slice('\0raw-source:'.length);
+ if (path.startsWith('/@fs/')) {
+ path = path.slice('/@fs'.length);
+ }
+ if (isDev) {
+ const devUrl = `${base}@raw-fs${path}`;
+ return `export default "${devUrl}";`;
+ }
+ const fileContent = readFileSync(path);
+ const ref = this.emitFile({
+ type: 'asset',
+ name: basename(path),
+ source: fileContent,
+ });
+ return {
+ code: `export default "__RAW-URL_${ref}__";`,
+ map: { version: 3, sources: [path], mappings: '' },
+ };
+ }
+ },
+
+ renderChunk(code, chunk) {
+ // Copied from vite assets code and simplified
+ let s, match;
+ while ((match = /__RAW-URL_([^_]+)__/g.exec(code))) {
+ s ||= new MagicString(code);
+ const ref = match[1];
+ const fileName = this.getFileName(ref);
+ chunk.viteMetadata!.importedAssets.add(fileName);
+ s.overwrite(match.index, match.index + match[0].length, base + fileName);
+ }
+ return s
+ ? {
+ code: s ? s.toString() : code,
+ map: doSourceMap ? s.generateMap({ hires: true }) : null,
+ }
+ : null;
+ },
+ };
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 823ccba2b308..a7a5682f013b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -335,6 +335,9 @@ importers:
leaflet:
specifier: 1.9.4
version: 1.9.4
+ magic-string:
+ specifier: 0.30.11
+ version: 0.30.11
openai:
specifier: 3.3.0
version: 3.3.0